最近在尝试unity的Socket建立,直接白嫖的代码同时来自己学习。可以先去看一下这个up讲解的视频是否满足自己需求。
Unity一个脚本解决UDP双端通讯_哔哩哔哩_bilibili
下面是代码:(我把其分成几部分来细说,有错误的地方还请指出!感谢大家~)
第一部分:头文件函数等
using UnityEngine;
using System.Collections;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Text.RegularExpressions;
using System.Collections.Generic;
此部分不做介绍。
第二部分:建立客户端通信插口
//定义客户端通信插口
Socket socket_c;
[Header("---------------------客户端--------------------")]
public string biji = "备注";
[Header("存放对方的接收ip")]
public string oppositeIP = "192.168.2.2";
[Header("存放对方的接收端口")]
public int oppositePort = 8888;
//对方服务端ip端口对
IPEndPoint oppositeIpEnd;
//广播的所有ip
List<string> allIPv4 = new List<string>();
这一部分主要建立两个变量分别存储对方ip地址和端口号。
其中IPEndPoint类,是将 IP 地址和服务端口号组合在一起。
第三部分:建立服务端通信插口
//定义服务端通信插口
Socket socket_s;
[Header("---------------------服务端--------------------")]
[Header("本机开放的端口")]
public int selfConnectPoint = 7777;
// [Header("本机ip自动获取")]
public static string selfAddress = "";
//本机服务端所监听的ip端口对
EndPoint selfListenEnd;
//监听线程
Thread connectThread;
第三部分是当unity作为服务端时,需要暴露出一个端口和ip地址,同时开启了一个监听线程。
(这里的ip地址后期会自动获取,详见下面)
第四部分:字符串接受面板显示
[Header("------------------入站的字符串---------------")]
public string recvStr;
//定义接受byte数组的长度
int recvLen = 0;
//接收到的byte数据
byte[] recvData = new byte[1024];
//要发送的的byte数据
byte[] sendData = new byte[1024];
第四部分,这里会在unity的spector面板显示传入的字符串,还有接受和发送的相关数组。
第五部分:Unity脚本初始化函数
void Start()
{
//去除ip 前后空格
oppositeIP = oppositeIP.Trim(); //去掉字符串的前后空格
//定义本机服务器的ip和端口对
if (selfAddress == "")
{
selfAddress = GetLocalIP();
}
selfAddress = selfAddress.Trim();
GetBroadcastIP(selfAddress);
//初始化
InitSocket();
}
第五部分,在这里分别对unity做客户端和服务端初始化。首先将对方的ip格式进行整理,即oppsoiteIP.Trim()。同时通过GetLocalIP()自定义函数获得本机的IP地址,将本机地址发送给广播函数,后期要以本机地址为基础向192.168.56.1到192.168.56.255发送广播数据报。
在这里同时进行socket()初始化。
第六部分:Socket()初始化
void InitSocket()
{
//作为客户端
//定义对方的ip和端口对
oppositeIpEnd = new IPEndPoint(IPAddress.Parse(oppositeIP), oppositePort);
socket_c = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//socketListen.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);//设置SOCKET允许多个SOCKET访问同一个本地IP地址和端口号
//作为服务端
//本机服务器监听的ip端口对
selfListenEnd = new IPEndPoint(IPAddress.Parse(selfAddress), selfConnectPoint);
socket_s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket_s.Bind(selfListenEnd);
//开启本机服务器监听线程
connectThread = new Thread(new ThreadStart(SocketReceive));
connectThread.Start();
//给目标服务端发送数据测试
SocketSendDefault(selfListenEnd.ToString() + ":first");
print("对方接到表示连接成功");
}
第六部分为socket的初始化:
对于客户端来说,将客户端的IP地址和端口统一并赋给oppositeEnd,建立一个客户端的socket_c。
对于服务端来说,将服务端的IP地址和端口统一并赋给selfListenEnd,建立一个服务端的socket_s,将此socket_s与selfListenEnd绑定。
开启一个监听线程,并传入参数SocketReceive.(下面会说)
通过SocketSendDefault()测试是否连接成功。
第七部分:服务器的监听
//本机服务器监听
void SocketReceive()
{
while (true)
{
recvData = new byte[1024];
recvLen = socket_s.ReceiveFrom(recvData, ref selfListenEnd);
recvStr = Encoding.UTF8.GetString(recvData, 0, recvLen);
Debug.Log("信息来自" + selfListenEnd.ToString() + ":" + recvStr);
getNew = true;
}
}
第七部分为服务器的监听
socket_s.ReceiveFrom()为接收数据并存储其数据来源客户端,并最终返回数组长度。
Encoding.UTF8.GetString将字符串用UTF8方法编码。
Debug.Log为Unity的控制台打印内容。
第八部分:作为客户端发送
//默认ip端口发送
public void SocketSendDefault(string sendStr)
{
//清空
sendData = new byte[1024];
//数据转换
sendData = Encoding.UTF8.GetBytes(sendStr);
//发送给指定服务端
socket_c.SendTo(sendData, sendData.Length, SocketFlags.None, oppositeIpEnd);
}
//自定义ip端口发送
public void SocketSendCustom(string sendStr, string ip, int port)
{
//清空
sendData = new byte[1024];
//数据转换
sendData = Encoding.UTF8.GetBytes(sendStr);
IPEndPoint aimIpEnd;
aimIpEnd = new IPEndPoint(IPAddress.Parse(ip), port);
//发送给指定服务端
socket_c.SendTo(sendData, sendData.Length, SocketFlags.None, aimIpEnd);
}
//默认ip端口群发
public void SendToAllDefault(string sendMsg)
{
StartCoroutine(SocketBroadcastSend(sendMsg, oppositePort));
}
//自定义ip端口群发
public void SendToAllCustom(string sendMsg, int port)
{
StartCoroutine(SocketBroadcastSend(sendMsg, port));
}
IEnumerator SocketBroadcastSend(string sendStr, int port)
{
//清空
byte[] sendData_thread = new byte[1024];
//数据转换
sendData_thread = Encoding.UTF8.GetBytes(sendStr);
IPEndPoint aimIpEnd;
foreach (string ip in allIPv4)
{
Debug.Log(ip);
aimIpEnd = new IPEndPoint(IPAddress.Parse(ip), port);
socket_c.SendTo(sendData_thread, sendData_thread.Length, SocketFlags.None, aimIpEnd);
}
yield return null;
}
第九部分:线程的关闭
//连接关闭
void SocketQuit()
{
//关闭线程
if (connectThread != null)
{
connectThread.Interrupt();
connectThread.Abort();
}
//最后关闭socket
if (socket_c != null)
socket_c.Close();
if (socket_s != null)
socket_s.Close();
StopAllCoroutines();
}
void OnApplicationQuit()
{
SocketQuit();
}
第十部分:Unity更新
void Update()
{
if (getNew == true)
{
getNew = false;
//执行接收文本事件
}
}
第十一部分:自动获取本机地址
public static string GetLocalIP()
{
try
{
string HostName = Dns.GetHostName(); //得到主机名
//返回一个IPHostEntry实例,其中包含有关 HostName 中指定的主机的地址信息
// IPHostEntry为 Internet 主机地址信息提供容器类
//方法 GetHostEntry 在 DNS 服务器中查询与主机名或 IP 地址关联的 IP 地址,并将 IP 地址解析为 IPHostEntry 实例。
IPHostEntry IpEntry = Dns.GetHostEntry(HostName);
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)
{
Debug.Log(IpEntry.AddressList[i].ToString());
return IpEntry.AddressList[i].ToString();
}
}
return "noIp";
}
catch
{
Debug.Log("ipGetFailed");
return ("ipGetFailed");
}
}
第十二部分:广播机制
public void GetBroadcastIP(string ip)
{
string[] nums = ip.Split('.');
string head = nums[0] + "." + nums[1] + "." + nums[2] + ".";
for (int i = 1; i < 255; i++)
{
allIPv4.Add(head + i.ToString());
}
}
ps源码见b站链接