using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
namespace DAMTool
{
public class UDPManager_Server : MonoBehaviour
{
public static UDPManager_Server Instance;
[Header("是否自动获取系统IP")]
public bool AutoSystemIP =true;
[Header("当前系统IP")]
public string ipAddress = "192.168.1.108";
[Header("当前系统端口号")]
public int ConnectPort = 7401;
[Header("是否使用外部目标IP和端口")]
bool UseOutTargetIPAndTargetPort = true;
[Header("目标系统IP")]
public string TargetIpAddress = "192.168.1.108";
[Header("目标系统端口号")]
public int TargetConnectPort = 7401;
[Header("接收到的信息")]
public string recvStr = "";
[Header("接收到的16进制字符")]
public string recvHexStr = "";
Socket socket;
EndPoint clientEnd;
IPEndPoint ipEnd;
string sendStr;
byte[] recvData = new byte[1024];
byte[] sendData = new byte[1024];
int recvLen;
Thread connectThread;
Thread confirmThread;
//发送指令确认队列
public List<string> ConfirmStrList = new List<string>();
//初始化
private void Awake()
{
Instance = this;
}
private void LateUpdate()
{
}
void Start()
{
if (AutoSystemIP)
{
//自动获取系统IP
ipAddress = GetIP();
//ipAddress = IPAddress.Any.ToString();
}
if(UseOutTargetIPAndTargetPort)
{
//加载外部IP和端口完成后初始化socket
LoadUDPData_OutIP_OutPort(InitSocket);
}
//在这里初始化server
//ConfirmStrList.Add("1234");
}
/// <summary>
/// 开启加载线程
/// </summary>
/// <param name="fun"></param>
public void LoadUDPData_OutIP_OutPort(UnityAction fun=null)
{
StartCoroutine(ReallyLoadUDPData(fun));
}
/// <summary>
/// 加载UDP数据
/// </summary>
/// <param name="fun">加载完毕执行的方法</param>
/// <returns></returns>
IEnumerator ReallyLoadUDPData(UnityAction fun=null)
{
string UDPDataFilePath = Path.Combine(Application.streamingAssetsPath, "SystemData");
UDPDataFilePath = Path.Combine(UDPDataFilePath, "UDPData.csv");
WWW www = new WWW(UDPDataFilePath);
yield return www;
if (www.isDone && !string.IsNullOrEmpty(www.text))
{
string[] content = www.text.Split('\n');
content[1] = content[1].Replace("\r", "");
TargetIpAddress= content[1].Split(',')[0];
TargetConnectPort=int.Parse( content[1].Split(',')[1]);
if (fun != null)
{
fun();
}
}
}
/// <summary>
/// 初始化套接字
/// </summary>
public void InitSocket()
{
//防止出现连接错误
uint IOC_IN = 0x80000000;
uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
Debug.Log(ipAddress + ":" + ConnectPort);
ipEnd = new IPEndPoint(IPAddress.Parse(ipAddress), ConnectPort);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(ipEnd);
#if UNITY_IPHONE
//IOS该代码会报错
#else
socket.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
#endif
//定义客户端
//IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
IPEndPoint sender = new IPEndPoint(IPAddress.Parse(TargetIpAddress), TargetConnectPort);
clientEnd = (EndPoint)sender;
print("等待连接数据");
//开启一个线程连接
connectThread = new Thread(new ThreadStart(SocketReceive));
confirmThread = new Thread(new ThreadStart(StrConfirm));
connectThread.Start();
confirmThread.Start();
}
/// <summary>
/// 套接字发送内容
/// </summary>
/// <param name="sendStr">发送内容</param>
/// <param name="needConfirm">是否加入确认数组,默认为不加入</param>
public void SocketSend(string sendStr,string IP="",int Port=0, bool needConfirm = false)
{
if(IP!=""&&Port!=0)
{
EndPoint clientEndNew;
IPEndPoint sender = new IPEndPoint(IPAddress.Parse(IP), Port);
clientEndNew = (EndPoint)sender;
Debug.Log("发送数据给指定目标,IP:"+IP+" Port:"+Port +" 内容:"+ sendStr);
sendData = new byte[1024];
sendData = Encoding.UTF8.GetBytes(sendStr);
socket.SendTo(sendData, sendData.Length, SocketFlags.None, clientEndNew);
if (needConfirm)
{
if (!ConfirmStrList.Contains(sendStr))
{
ConfirmStrList.Add(sendStr);
}
}
}
else
{
Debug.Log("发送数据给指定目标,IP:" + TargetIpAddress + " Port:" + TargetConnectPort + " 内容:" + sendStr);
sendData = new byte[1024];
sendData = Encoding.UTF8.GetBytes(sendStr);
socket.SendTo(sendData, sendData.Length, SocketFlags.None, clientEnd);
if (needConfirm)
{
if (!ConfirmStrList.Contains(sendStr))
{
ConfirmStrList.Add(sendStr);
}
}
}
}
/// <summary>
/// 发送16进制指令
/// </summary>
/// <param name="sendStr">发送内容</param>
public void SocketSendHex(string sendStr, string IP = "", int Port = 0, bool needConfirm = false)
{
if (IP != "" && Port != 0)
{
EndPoint clientEndNew;
IPEndPoint sender = new IPEndPoint(IPAddress.Parse(IP), Port);
clientEndNew = (EndPoint)sender;
Debug.Log("发送16进制数据,IP:" + IP + " Port:" + Port + " 内容:" + sendStr);
sendData = new byte[1024];
sendData = DAMDataManager.Instance.HexStringSToByteArray(sendStr);
socket.SendTo(sendData, sendData.Length, SocketFlags.None, clientEndNew);
if (needConfirm)
{
if (!ConfirmStrList.Contains(sendStr))
{
ConfirmStrList.Add(sendStr);
}
}
}
else
{
Debug.Log("发送16进制数据,IP:" + TargetIpAddress + " Port:" + TargetConnectPort + " 内容:" + sendStr);
sendData = new byte[1024];
sendData = DAMDataManager.Instance.HexStringSToByteArray(sendStr);
socket.SendTo(sendData, sendData.Length, SocketFlags.None, clientEnd);
if (needConfirm)
{
if (!ConfirmStrList.Contains(sendStr))
{
ConfirmStrList.Add(sendStr);
}
}
}
}
//服务器接收
void SocketReceive()
{
recvData = new byte[1024];
while (true)
{
recvLen = socket.ReceiveFrom(recvData, ref clientEnd);
recvStr = Encoding.UTF8.GetString(recvData, 0, recvLen);
recvHexStr = Byte2Hex(recvData, recvLen);
Debug.Log("收到的信息 " + recvStr);
Debug.Log("收到的Hex字符串" + recvHexStr+" "+clientEnd);
//SocketSend(recvStr);
}
}
//字符确认
void StrConfirm()
{
while (true)
{
if (recvStr != "")
{
//Debug.Log(ConfirmStrList.Contains(recvStr));
if (ConfirmStrList.Contains(recvStr))
{
ConfirmStrList.Remove(recvStr);
Debug.Log("收到字符串" + recvStr);
recvStr = "";
}
}
}
}
//连接关闭
void SocketQuit()
{
//关闭线程
if (connectThread != null)
{
connectThread.Interrupt();
connectThread.Abort();
}
if (confirmThread != null)
{
confirmThread.Interrupt();
confirmThread.Abort();
}
//最后关闭socket
if (socket != null)
socket.Close();
Debug.LogWarning("断开连接");
}
//重新连接
// Use this for initialization
public void ReConnectServer()
{
SocketQuit();
InitSocket();
}
void OnApplicationQuit()
{
SocketQuit();
}
/// <summary>
/// 获取IP,安卓端不可用
/// </summary>
/// <param name="Addfam"></param>
/// <returns></returns>
public string GetIP(ADDRESSFAM Addfam)
{
//Return null if ADDRESSFAM is Ipv6 but Os does not support it
if (Addfam == ADDRESSFAM.IPv6 && !Socket.OSSupportsIPv6)
{
return null;
}
string output = "";
foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces())
{
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
NetworkInterfaceType _type1 = NetworkInterfaceType.Wireless80211;
NetworkInterfaceType _type2 = NetworkInterfaceType.Ethernet;
if ((item.NetworkInterfaceType == _type1 || item.NetworkInterfaceType == _type2) && item.OperationalStatus == OperationalStatus.Up)
#endif
{
foreach (UnicastIPAddressInformation ip in item.GetIPProperties().UnicastAddresses)
{
//IPv4
if (Addfam == ADDRESSFAM.IPv4)
{
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
{
output = ip.Address.ToString();
Debug.Log("" + output);
}
}
//IPv6
else if (Addfam == ADDRESSFAM.IPv6)
{
if (ip.Address.AddressFamily == AddressFamily.InterNetworkV6)
{
output = ip.Address.ToString();
}
}
}
}
}
return output;
}
//或取系统IP
public string GetIP()
{
string AddressIP = string.Empty;
#if UNITY_IPHONE
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces(); ;
foreach (NetworkInterface adapter in adapters)
{
if (adapter.Supports(NetworkInterfaceComponent.IPv4))
{
UnicastIPAddressInformationCollection uniCast = adapter.GetIPProperties().UnicastAddresses;
if (uniCast.Count > 0)
{
foreach (UnicastIPAddressInformation uni in uniCast)
{
//得到IPv4的地址。 AddressFamily.InterNetwork指的是IPv4
if (uni.Address.AddressFamily == AddressFamily.InterNetwork)
{
AddressIP = uni.Address.ToString();
}
}
}
}
}
return AddressIP;
#endif
string IP = "";
IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName()); //Dns.GetHostName()获取本机名Dns.GetHostAddresses()根据本机名获取ip地址组
foreach (IPAddress ip in ips)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
IP = ip.ToString(); //ipv4
}
else if (ip.AddressFamily == AddressFamily.InterNetworkV6)
{
IP = ip.ToString(); //ipv6
}
}
return IP;
}
//byte转16进制数
static string Byte2Hex(byte wbyte)
{
string ret = null;
var arr = "0123456789ABCDEF";
var len = arr.Length;
ret += arr[wbyte / len].ToString();
ret += arr[wbyte % len].ToString();
return ret;
}
//btye数组转字符,需要数组长度
static string Byte2Hex(byte[] _byte, int _Length)
{
string HexStr = null;
for (int i = 0; i < _Length; i++)
{
HexStr += Byte2Hex(_byte[i]);
}
return HexStr;
}
}
}
public enum ADDRESSFAM
{
IPv4, IPv6
}
已用于很多项目,效果不错,用的话需要稍作修改
补充
public void InitSocket()
{
//防止出现连接错误
//uint IOC_IN = 0x80000000;
//uint IOC_VENDOR = 0x18000000;
//uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
Debug.Log(ipAddress + ":" + ConnectPort);
ipEnd = new IPEndPoint(IPAddress.Parse(ipAddress), ConnectPort);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(ipEnd);
#if UNITY_IPHONE
//IOS该代码会报错
#else
//socket.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
#endif
//定义客户端
//IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
IPEndPoint sender = new IPEndPoint(IPAddress.Parse(TargetIpAddress1), TargetConnectPort1);
clientEnd = (EndPoint)sender;
print("等待连接数据");
//开启一个线程连接
connectThread = new Thread(new ThreadStart(SocketReceive));
confirmThread = new Thread(new ThreadStart(StrConfirm));
connectThread.Start();
confirmThread.Start();
}
在安卓端如果出现无法收到消息,但是可以发送消息的情况,将初始化方法中的防止出现连接错误的代码注释掉就可以了