近来有需求如下:局域网内两个人合作拆装一个东西,比如汽车引擎。于是开始学习socket通信。还好网上这个教程已经很多了,整理了一下自己拿过来用了。
最开始用的是UDP,因为想着客户端A做了什么直接扔给服务器然后服务器再扔给客户端B(反过来也一样)就完事了。
udp客户端和服务器端的基类:
using UnityEngine;
public abstract class BaseUDP : MonoBehaviour {
public abstract void InitSocket();
public abstract void SocketSend(string sender);
public abstract void SocketReceive();
}
服务器端:
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using LitJson;
using System.Collections.Generic;
using UnityEngine.UI;
public class UDPServer : BaseUDP
{
/// <summary>
/// 设置服务器IP
/// </summary>
public string ipAddress;
/// <summary>
/// 服务器侦听端口
/// </summary>
public int ConnectPort;
/// <summary>
/// 服务器接收到的消息
/// </summary>
public string recvStr=null;
/// <summary>
/// 服务器接收到的IP和端口信息,记录下来
/// </summary>
private List<EndPoint> mEndPoint = null;
/// <summary>
/// 声明套接字
/// </summary>
private Socket socket;
/// <summary>
/// 客户端地址
/// </summary>
private EndPoint clientEnd;
/// <summary>
/// 服务器端地址
/// </summary>
private IPEndPoint ipEnd;
/// <summary>
/// 服务器发送的消息
/// </summary>
private string sendStr;
/// <summary>
/// 接收和发送消息字节流
/// </summary>
byte[] recvData = new byte[1024];
byte[] sendData = new byte[1024];
/// <summary>
/// 接收消息的长度
/// </summary>
private int recvLen;
/// <summary>
/// 侦听客户端的线程
/// </summary>
private Thread connectThread;
public Text mInfo;
private Dictionary<string, EndPoint> mClients=new Dictionary<string, EndPoint>();
//初始化
public override void InitSocket()
{
//用指定的端口和地址初始化IPEndPoint实例
ipEnd = new IPEndPoint(IPAddress.Parse(ipAddress), ConnectPort);
//初始化socket(AddressFamily.InterNetwork,指定socket的寻址方案是IPV4)
//SocketType.Dgram 支持数据报,即最大长度固定(通常很小)的无连接、不可靠消息
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(ipEnd);
//定义客户端
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
clientEnd = (EndPoint)sender;
print("等待连接数据");
//开启一个线程连接
connectThread = new Thread(new ThreadStart(SocketReceive));
connectThread.Start();
}
//服务器发送消息
public override void SocketSend(string sendStr)
{
sendData = new byte[1024];
sendData = Encoding.UTF8.GetBytes(sendStr);
foreach (var item in mClients)
{
if (item.Key!=clientEnd.ToString())
{
socket.SendTo(sendData, sendData.Length, SocketFlags.None, item.Value);
Debug.Log("发送" + sendStr + "给" + item.ToString());
}
}
}
//服务器接收
public override void SocketReceive()
{
while (true)
{
recvData = new byte[1024];
recvLen = socket.ReceiveFrom(recvData, ref clientEnd);
recvStr = Encoding.UTF8.GetString(recvData, 0, recvLen);
Debug.Log("服务器接收到的:"+recvStr);
Debug.Log(clientEnd.ToString());
if (mClients==null||!mClients.ContainsKey(clientEnd.ToString()))
{
mClients.Add(clientEnd.ToString(), clientEnd);
}
SocketSend(recvStr);
}
}
//连接关闭
void SocketQuit()
{
//关闭线程
if (connectThread != null)
{
connectThread.Interrupt();
connectThread.Abort();
}
//最后关闭socket
if (socket != null)
socket.Close();
Debug.LogWarning("断开连接");
}
// 这里是用配表的方式来加载ip和端口
void Start()
{
Record record1 = TableManager.mApplicationTable["IP"];
ipAddress = record1["Value"];
Record record2 = TableManager.mApplicationTable["Port"];
ConnectPort = record2.GetCell("Value");
InitSocket(); //在这里初始化server
}
private void Update()
{
//用来显示接收到的消息
mInfo.text = recvStr;
}
//应用退出时socket也要关闭
void OnApplicationQuit()
{
SocketQuit();
}
}
客户端:
using UnityEngine;
using System.Collections;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using LitJson;
public class UDPClient : BaseUDP
{
/// <summary>
/// 客户端接收到的信息
/// </summary>
public string mRecvStr;
/// <summary>
/// 服务器端IP
/// </summary>
private string mUDPServerIP;
//服务器端监听端口
private int mUDPServerPort;
/// <summary>
/// 要发送的信息
/// </summary>
private string mSendStr = "";
/// <summary>
/// socket
/// </summary>
private Socket socket;
/// <summary>
/// 服务器端地址
/// </summary>
private EndPoint serverEnd;
private IPEndPoint ipEnd;
//接收信息和发送信息的缓冲字节区
private byte[] recvData = new byte[1024];
private byte[] sendData = new byte[1024];
//接收信息长度
private int recvLen = 0;
//接收信息的线程
private Thread connectThread;
void Start()
{
Record record1 = TableManager.mApplicationTable["IP"];
mUDPServerIP = record1["Value"];
Record record2 = TableManager.mApplicationTable["Port"];
mUDPServerPort = record2["Value"];
mUDPServerIP = mUDPServerIP.Trim();
InitSocket();
}
public override void InitSocket()
{
ipEnd = new IPEndPoint(IPAddress.Parse(mUDPServerIP),mUDPServerPort );
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
serverEnd = (EndPoint)sender;
print("等待连接");
SocketSend(mSendStr);
print("连接");
//开启一个线程连接
connectThread = new Thread(new ThreadStart(SocketReceive));
connectThread.Start();
}
public override void SocketSend(string sendStr)
{
//清空
sendData = new byte[1024];
//数据转换
sendData = Encoding.UTF8.GetBytes(sendStr);
//发送给指定服务端
socket.SendTo(sendData, sendData.Length, SocketFlags.None, ipEnd);
}
//接收服务器信息
public override void SocketReceive()
{
while (true)
{
recvData = new byte[1024];
try
{
recvLen = socket.ReceiveFrom(recvData, ref serverEnd);
}
catch (Exception e)
{
}
print("信息来自: " + serverEnd.ToString());
if (recvLen > 0)
{
mRecvStr = Encoding.UTF8.GetString(recvData, 0, recvLen);
Controller.instance.s = mRecvStr;
}
}
}
//连接关闭
void SocketQuit()
{
//关闭线程
if (connectThread != null)
{
connectThread.Interrupt();
connectThread.Abort();
}
//最后关闭socket
if (socket != null)
socket.Close();
}
void OnApplicationQuit()
{
SocketQuit();
}
}
后来测试的时候发现了一个问题,客户端A拖拽物体有时候和客户端B的物体不一定能同步。。。。。。于是改用TCP。
TCP基类:
using UnityEngine;
public abstract class BaseTCP : MonoBehaviour {
public abstract void InitSocket();
public abstract void SocketSend(string sender);
public abstract void SocketReceive();
public abstract void SocketConnect();
}
服务器端:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using UnityEngine.UI;
public class TCPServer : BaseTCP {
private Socket mServerSocket;
private List<Socket> mClinetSockets=new List<Socket>();
private IPEndPoint mIPEndPoint;
private string mServerIPAddress;
private int mServerPort;
private string mRecvStr;
private string mSendStr;
private byte[] mRecvData = new byte[1024];
private byte[] mSendData = new byte[1024];
private int mRecvLength;
private Thread mServerThread;
public Text mText;
/// <summary>
/// 初始化socket,绑定IP和端口
/// </summary>
public override void InitSocket()
{
//监听端口是mServerPort,监听所有IP
mIPEndPoint = new IPEndPoint(IPAddress.Any,mServerPort);
//初始化一个socket,指定socket的寻址方案是IPV4,支持可靠双向的字节流,协议类型是IPV4
mServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//socket绑定一个地址
mServerSocket.Bind(mIPEndPoint);
//最大监听数10个
mServerSocket.Listen(10);
mServerThread = new Thread(new ThreadStart(SocketConnect));
mServerThread.Start();
}
public override void SocketReceive()
{
//SocketConnect();
//while (true)
//{
// mRecvData = new byte[1024];
// mRecvLength = mClinetSocket.Receive(mRecvData);
// if (mRecvLength==0)
// {
// SocketConnect();
// continue;
// }
// mRecvStr = Encoding.ASCII.GetString(mRecvData, 0, mRecvLength);
// print(mRecvStr);
// mSendStr = "来自服务器:" + mRecvStr;
// SocketSend(mSendStr);
//}
}
/// <summary>
/// 接收客户端消息的线程
/// </summary>
/// <param name="obj">客户端socket(线程只能传递object类型)</param>
public void RecvMsg(object obj)
{
Socket client = obj as Socket;
mRecvData = new byte[1024];
IPEndPoint clinetPoint = client.RemoteEndPoint as IPEndPoint;
try
{
while (true)
{
mRecvLength = client.Receive(mRecvData);
mRecvStr = Encoding.ASCII.GetString(mRecvData, 0, mRecvLength);
SocketSend(mRecvStr);
}
}
catch (System.Exception)
{
throw;
}
}
/// <summary>
/// 给指定客户端发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="socket"></param>
public void SocketSend(string sender,Socket socket)
{
for (int i = 0; i < mClinetSockets.Count; i++)
{
if (mClinetSockets[i]!=socket)
{
try
{
mSendData = new byte[1024];
mSendData = Encoding.ASCII.GetBytes(sender);
mClinetSockets[i].Send(mSendData);
}
catch (System.Exception)
{
throw;
}
}
}
}
/// <summary>
/// 给所有客户端传递消息
/// </summary>
/// <param name="sender"></param>
public override void SocketSend(string sender)
{
for (int i = 0; i < mClinetSockets.Count; i++)
{
try
{
mSendData = new byte[1024];
mSendData = Encoding.ASCII.GetBytes(sender);
mClinetSockets[i].Send(mSendData);
}
catch (System.Exception)
{
throw;
}
}
}
/// <summary>
/// 连接的线程,每接收到一个客户端的socket就存入自己的列表然后开启线程接收消息
/// </summary>
public override void SocketConnect()
{
while (true)
{
try
{
Socket client = mServerSocket.Accept();
mClinetSockets.Add(client);
IPEndPoint endPoint = client.RemoteEndPoint as IPEndPoint;
print(endPoint.Address.ToString() + endPoint.Port.ToString());
Thread recvThread = new Thread(RecvMsg);
recvThread.Start(client);
}
catch (System.Exception)
{
throw;
}
}
}
/// <summary>
/// 退出的时候要清空自己的表,关闭线程
/// </summary>
private void SocketQuit()
{
if (mClinetSockets.Count>0)
{
for (int i = 0; i < mClinetSockets.Count; i++)
{
mClinetSockets[i].Close();
}
}
mClinetSockets.Clear();
mServerSocket.Close();
if (mServerThread!=null)
{
mServerThread.Interrupt();
mServerThread.Abort();
}
}
// Use this for initialization
void Start () {
TableManager.Self.SetTable();
Record record1 = TableManager.mApplicationTable["IP"];
mServerIPAddress = record1["Value"];
Record record2 = TableManager.mApplicationTable["Port"];
mServerPort = record2.GetCell("Value");
InitSocket();
}
private void Update()
{
mText.text = mRecvStr;
}
private void OnApplicationQuit()
{
SocketQuit();
}
}
客户端:
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class TCPClient : BaseTCP {
private Socket mServerSocket;
private Socket mClientSocket;
private IPEndPoint mIPEndPoint;
private string mServerIPAddress;
private int mServerPort;
private string mRecvStr;
private string mSendStr;
private byte[] mRecvData = new byte[1024];
private byte[] mSendData = new byte[1024];
private int mRecvLength;
private Thread mRecvThread;
public override void InitSocket()
{
mIPEndPoint = new IPEndPoint(IPAddress.Parse(mServerIPAddress), mServerPort);
mRecvThread = new Thread(new ThreadStart(SocketReceive));
mRecvThread.Start();
}
public override void SocketReceive()
{
SocketConnect();
while (true)
{
mRecvData = new byte[1024];
mRecvLength = mServerSocket.Receive(mRecvData);
if (mRecvLength==0)
{
SocketConnect();
continue;
}
mRecvStr = Encoding.ASCII.GetString(mRecvData, 0, mRecvLength);
Controller.instance.s = mRecvStr;
}
}
public override void SocketConnect()
{
if (mServerSocket != null)
{
mServerSocket.Close();
}
mServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
mServerSocket.Connect(mIPEndPoint);
mRecvLength = mServerSocket.Receive(mRecvData);
mRecvStr = Encoding.ASCII.GetString(mRecvData, 0, mRecvLength);
}
public override void SocketSend(string sender)
{
mSendData = new byte[1024];
mSendData = Encoding.ASCII.GetBytes(sender);
mServerSocket.Send(mSendData, mSendData.Length, SocketFlags.None);
}
void SocketQuit()
{
//关闭线程
if (mRecvThread != null)
{
mRecvThread.Interrupt();
mRecvThread.Abort();
}
//最后关闭服务器
if (mServerSocket != null)
mServerSocket.Close();
print("diconnect");
}
// Use this for initialization
void Start () {
Record record1 = TableManager.mApplicationTable["IP"];
mServerIPAddress = record1["Value"];
Record record2 = TableManager.mApplicationTable["Port"];
mServerPort = record2.GetCell("Value");
InitSocket();
}
private void OnApplicationQuit()
{
SocketQuit();
}
}
运行gif:
那一堆字符是自己写的一个类,把物体的id和位置信息、角度信息、缩放信息封装一下,好用litjson进行传输和解析
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectInfo {
public string id;
public string tx;
public string ty;
public string tz;
public string qx;
public string qy;
public string qz;
public string qw;
public string sx;
public string sy;
public string sz;
public ObjectInfo() { }
public ObjectInfo(string id,Vector3 v3, Quaternion quaternion, Vector3 scale)
{
this.id = id;
tx = v3.x.ToString();
ty = v3.y.ToString();
tz = v3.z.ToString();
qx = quaternion.x.ToString();
qy = quaternion.y.ToString();
qz = quaternion.z.ToString();
qw = quaternion.w.ToString();
sx = scale.x.ToString();
sy = scale.y.ToString();
sz = scale.z.ToString();
}
}
为什么都用string呢?因为json不支持float类型的 。。。。。。支持double,但是呢同样要转型,索性都用string了。物体拖拽旋转啥的脚本我就不贴了,篇幅太长了。