//通过网络接口描述来获取IP
public static string GetLocalIP_LAN80211()
{
//GetAllNetworkInterfaces 返回描述本地计算机上的网络接口的对象。
foreach (var interfaces in NetworkInterface.GetAllNetworkInterfaces())
{
//NetworkInterfaceType 网络接口的类型,Wireless80211 网络接口使用无线 LAN 连接(IEEE 802.11 标准)。
//OperationalStatus 指定网络接口的操作状态,Up 网络接口已运行,可以传输数据包。
if (interfaces.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 && interfaces.OperationalStatus == OperationalStatus.Up)
{
//GetIPProperties 返回描述此网络接口的配置的对象。
//IPInterfaceProperties 提供有关支持 Internet 协议版本 4 (IPv4) 或 Internet 协议版本 6 (IPv6) 的网络接口的信息。
//UnicastAddresses 获取分配给此接口的单播地址。
foreach (var ip in interfaces.GetIPProperties().UnicastAddresses)
{
//InterNetwork IP 版本 4 的地址。InterNetworkV6 IP 版本 6 的地址。
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
{
//192. 开头保证获取的是内网地址
if (ip.Address.ToString().StartsWith("192."))
return ip.Address.ToString();
}
}
}
}
return string.Empty;
}
//通过DNS解析本地IP
public static string GetLocalIP_V2()
{
try
{
//获取本地计算机的主机名。
string HostName = Dns.GetHostName();
//将主机名或 IP 地址解析为 IPHostEntry 实例。
IPHostEntry IpEntry = Dns.GetHostEntry(HostName);
//AddressList 获取或设置与主机关联的 IP 地址列表。
for (int i = 0; i < IpEntry.AddressList.Length; i++)
{
//从IP地址列表中筛选出IPv4类型的IP地址
//AddressFamily.InterNetwork表示此IP为IPv4,
//AddressFamily.InterNetworkV6表示此地址为IPv6类型
if (IpEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
{
string ip = "";
ip = IpEntry.AddressList[i].ToString();
return IpEntry.AddressList[i].ToString();
}
}
return "";
}
catch (Exception ex)
{
return ex.Message;
}
}
namespace System.Net.Sockets
class UdpClient
详细描述(转发)
简单来说,传输层,无连接,不可靠,广播多播解决方案
(如果不考虑接收广播,发送方可以随便定个端口)
public async void ListenAsync()
{
while (true)
{
if (udpClient != null)
{
try
{
OnGetMessage(await udpClient.ReceiveAsync());
}
catch
{
break;
}
}
}
}
private void OnGetMessage(UdpReceiveResult receive)
{
string msg = Encoding.UTF8.GetString(receive.Buffer);
//UnityEngine.Debug.Log($"OnGetMessage ip:{receive.RemoteEndPoint} msg:{msg}");
EventOnGetMessage?.Invoke(receive.RemoteEndPoint, msg);
}
如果在异步执行中,调用 UdpClient.Close 后,await 那一行代码会报错,不过好像对功能没影响
详细描述(转发)
Socket 跟 TCP/IP 的关系
讲个笑话
问:socket 跟 websocket 什么关系?
答:跟 java 和 JavaScript 的关系一样。
namespace System.Net.Sockets
class Socket
try
{
//1.0 实例化套接字(IP4寻找协议,流式协议,TCP协议)
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//IP为本地IP,端口
IPAddress ip = IPAddress.Parse(IP);
IPEndPoint endPoint = new IPEndPoint(ip, Port);
//绑定套接字
_socket.Bind(endPoint);
//设最大链接数量
_socket.Listen(MaxValue);
//开始监听
ListenClientConnectAsync();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
throw;
}
Socket clientSocket = await _socket.AcceptAsync();
一个同步(阻塞)、一个异步,都是提供操作新接入客户端的一个 Socket 对象。换句话说每一个获取到的 Socket 对应一个连接的客户端,要收、发客户端消息,就需要操作指定的 Socket 对象。
private void ReceiveMessage(object socket)
{
Socket clientSocket = socket as Socket;
string tmpIP = clientSocket.RemoteEndPoint.ToString();
if (clientSocket != null)
dicClientSocket.Add(tmpIP, clientSocket);
while (clientSocket != null&&clientSocket.Connected)
{
try
{
int length = clientSocket.Receive(buffer);
string msg = Encoding.UTF8.GetString(buffer, 0, length);
if (!CheckSocketIsConnect(clientSocket))
throw new Exception("已断开链接");
//收到客户端消息事件
OnGetMsg(clientSocket.RemoteEndPoint.ToString(), msg);
}
catch (Exception e)
{
if (clientSocket != null)
{
//客户端断开链接事件
OnClientDisconnect(tmpIP);
dicClientSocket.Remove(tmpIP);
//clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
clientSocket = null;
}
break;
}
}
}
客户端异常断开链接后,会不停的 Receive 到空字符,就手动判断了客户端链接状态 public void StartClient()
{
try
{
//1.0 实例化套接字(IP4寻址地址,流式传输,TCP协议)
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//IP为服务器 IP ,端口
IPAddress ip = IPAddress.Parse(IP);
IPEndPoint endPoint = new IPEndPoint(ip, Port);
//建立链接
_socket.Connect(endPoint);
Thread thread = new Thread(ReceiveMessage);
thread.Start(_socket);
}
catch (Exception e)
{
UnityEngine.Debug.LogError("链接失败 : " + e);
throw;
}
}
先判断是否是连接状态,如果是需要先调用 Socket.Shutdown(SocketShutdown) 断开链接
然后调用 Socket.Close
try
{
aSocket.Shutdown(SocketShutdown.Both);
}
finally
{
aSocket.Close();
}
Socket.Poll(Int32, SelectMode)
Socket.Connected
public static bool CheckSocketIsConnect(Socket socket)
{
if (socket == null) return false;
//SelectRead 如果已调用 Listen(Int32) 并且有挂起的连接,则为 true。
//- 或 - 如果有数据可供读取,则为 true。
//- 或 - 如果连接已关闭、重置或终止,则返回 true; 否则,返回 false。
bool part1 = socket.Poll(1000, SelectMode.SelectRead);
//获取已经从网络接收且可供读取的数据量。
bool part2 = socket.Available == 0;
if ((part1 && part2) || !socket.Connected)
return false;
else
return true;
}
使用同一个路由器,或者同一个手机热点