Unity开发之网络一TCP服务端实现
Unity开发之网络—集成Protobuf Unity开发之网络一HTTP客户端实现 Unity开发之网络一HTTP服务端实现 Unity开发之网络一TCP客户端实现 下面介绍服务端,共涉及三个类:SocketModule,SocketEngine,ServerSocketItem SocketModule:同客户端,定义接口及数据包结构等定义; SocketEngine:服务端接收客户端连接等处理; ServerSocketItem:单个客户端数据交互的处理; 下面主要介绍SocketEngine 开启服务
public bool StartServer(string ip, string port)
{
m_bService = true;
// 创建负责监听的套接字
m_hServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
m_hServerSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
IPAddress address = IPAddress.Parse(ip);
// 创建包含ip和端口号的网络节点对象;
IPEndPoint endPoint = new IPEndPoint(address, int.Parse(port));
try
{
//将负责监听的套接字绑定到唯一的ip和端口上;
m_hServerSocket.Bind(endPoint);
}
catch (SocketException se)
{
onShowMsg("异常:" + se.Message);
return false;
}
// 设置监听队列的长度;
m_hServerSocket.Listen(10);
// 创建负责监听的线程;
m_SocketAcceptThread = new Thread(ListenSocket);
m_SocketAcceptThread.IsBackground = true;
m_SocketAcceptThread.Start();
//检测线程
m_dwTickCount = 0;
m_SocketDetectThread = new Thread(DetectSocket);
m_SocketDetectThread.IsBackground = true;
m_SocketDetectThread.Start();
onShowMsg("服务启动成功!");
return true;
}
服务启动成功后,会启动监听线程,接收客户端的连接
/// <summary>
/// 监听客户端请求的方法;
/// </summary>
void ListenSocket()
{
while (IsService())
{
try
{
// 开始监听客户端连接请求,Accept方法会阻断当前的线程
Socket connect = m_hServerSocket.Accept();
if (connect.Connected && !m_SocketRSThread.ContainsKey(connect.RemoteEndPoint.ToString()))
{
//创建接收对象
ServerSocketItem socketItem = new ServerSocketItem((short)m_StorageSocketItem.Count, this);
socketItem.Attach(connect);
// 将与客户端连接的 套接字 对象添加到集合中
m_StorageSocketItem.Add(socketItem);
onSocketConnect(true, socketItem.GetClientAddr());
if (m_pIServerSocketItemSink != null)
{
m_pIServerSocketItemSink.OnSocketAcceptEvent(socketItem);
}
Thread thread = new Thread(RecvSocket);
thread.IsBackground = true;
thread.Start(socketItem);
m_SocketRSThread.Add(connect.RemoteEndPoint.ToString(), thread);
}
}
catch(Exception e)
{
Console.WriteLine(this.GetType().ToString() + ":" + e.ToString());
}
}
}
void RecvSocket(object item)
{
ServerSocketItem socketItem = item as ServerSocketItem;
while (IsService() && socketItem.IsValidSocket())
{
try
{
//length = socketClient.Receive(arrMsgRec); // 接收数据,并返回数据的长度;
socketItem.RecvData();
}
catch (SocketException se)
{
onShowMsg("异常:" + se.Message);
RemoveSocketItem(socketItem);
break;
}
catch (Exception e)
{
onShowMsg("异常:" + e.Message);
RemoveSocketItem(socketItem);
break;
}
}
}
接收到客户端的连接请求后,启动一个读写线程,创建ServerSocketItem变量与之对应 心跳检测
void DetectSocket()
{
while (IsService())
{
//设置间隔
Thread.Sleep(500);
m_dwTickCount += 500L;
if (m\_dwTickCount > SocketModule.TIME\_DETECT\_SOCKET && m\_StorageSocketItem != null)
{
m_dwTickCount = 0;
for (int i = 0; i < m_StorageSocketItem.Count; ++i)
{
var socketItem = m_StorageSocketItem\[i\];
if (socketItem != null)
{
CMD\_KN\_DetectSocket detectSocket = new CMD\_KN\_DetectSocket();
detectSocket.dwSendTickCount = 0;// int.Parse(ZYSoft.Utils.Time.GetTimeStamp(DateTime.Now));
byte\[\] cbDataBuffer = detectSocket.Encode();
socketItem.SendData(cbDataBuffer, (short)cbDataBuffer.Length, SocketModule.MDM\_KN\_COMMAND, SocketModule.SUB\_KN\_DETECT_SOCKET, socketItem.GetRountID());
}
}
}
}
}
网络引擎和应用层交互通过IServerSocketItemDelegate接口
//连接对象回调接口
public interface IServerSocketItemDelegate
{
//应答消息
bool OnSocketAcceptEvent(ServerSocketItem serverSocketItem);
//读取消息
bool OnSocketReadEvent(CMD_Command Command, byte\[\] pBuffer, short wDataSize, ServerSocketItem serverSocketItem);
//关闭消息
bool OnSocketCloseEvent(ServerSocketItem serverSocketItem);
};
示例
public class AttemperEngine : IServerSocketItemDelegate
{
.....................................
public bool OnSocketAcceptEvent(ServerSocketItem serverSocketItem)
{
return true;
}
public bool OnSocketReadEvent(CMD_Command Command, byte\[\] pBuffer, short wDataSize, ServerSocketItem serverSocketItem)
{
return false;
}
public bool OnSocketCloseEvent(ServerSocketItem serverSocketItem)
{
return false;
}
}
ServerSocketItem处理与客户端数据的接收与发送 接收
//接收操作
public void RecvData()
{
//效验变量
//Debug.Assert(m_bRecvIng == false);
if(m_hSocket == null) return;
//判断关闭
if (m_bCloseIng == true)
{
CloseSocket(m_wRountID);
return;
}
//设置变量
m_bRecvIng = true;
m_dwRecvTickCount = DateTime.Now.Millisecond / 1000;
//判断关闭
if (m_hSocket == null)
{
CloseSocket(m_wRountID);
}
//接收数据
int iRetCode = m\_hSocket.Receive(m\_cbRecvBuf, m\_wRecvSize, (int)(m\_cbRecvBuf.GetLength(0) - m_wRecvSize), SocketFlags.None);
if (iRetCode <= 0)
{
CloseSocket(m_wRountID);
}
//接收完成
m_wRecvSize += iRetCode;
//byte\[\] cbBuffer = new byte\[SocketModule.SOCKET_BUFFER\];
CMD\_Head pHead = new CMD\_Head();
//pHead.Decode(m_cbRecvBuf);
//处理数据
while (m\_wRecvSize >= CMD\_Head.sizeof\_CMD\_Head)
{
pHead.Decode(m_cbRecvBuf);
//效验数据
short wPacketSize = pHead.cmdInfo.wDataSize;
if (wPacketSize > SocketModule.SOCKET_BUFFER) break;// throw ("数据包超长");
if (wPacketSize < CMD\_Head.sizeof\_CMD_Head) break;// throw ("数据包非法");
if (pHead.cmdInfo.cbMessageVer != SocketModule.SOCKET_VER) break;// throw ("数据包版本错误");
if (m_wRecvSize < wPacketSize) break;
byte\[\] cbBuffer = new byte\[wPacketSize\];
//提取数据
Buffer.BlockCopy(m_cbRecvBuf, 0, cbBuffer, 0, wPacketSize);
short wRealySize = CrevasseBuffer(cbBuffer, wPacketSize);
m_dwRecvPacketCount++;
//删除缓存数据
m_wRecvSize -= wPacketSize;
Buffer.BlockCopy(m\_cbRecvBuf, wPacketSize, m\_cbRecvBuf, 0, m_wRecvSize);
//解释数据
short wDataSize = (short)(wRealySize - CMD\_Head.sizeof\_CMD_Head);
pHead = new CMD_Head();
pHead.Decode(cbBuffer);
//m_strRecvSession = Encoding.Default.GetString(pHead.cbSession);
CMD_Command Command = pHead.command;
//内核命令
if (Command.wMainCmdID == SocketModule.MDM\_KN\_COMMAND)
{
switch (Command.wSubCmdID)
{
case SocketModule.SUB\_KN\_DETECT_SOCKET: //网络检测
{
break;
}
default: break;// throw ("非法命令码");
}
}
else
{
//数据消息处理
Buffer.BlockCopy(cbBuffer, CMD\_Head.sizeof\_CMD_Head, cbBuffer, 0, wDataSize);
m_pIServerSocketItemSink.OnSocketReadEvent(Command, cbBuffer, wDataSize, this);
}
}
}
发送
//发送函数
public bool SendData(byte\[\] pData, short wDataSize, short wMainCmdID, short wSubCmdID, short wRountID) {
//效验参数
//Debug.Assert(wDataSize <= SocketModule.SOCKET_PACKAGE);
//效验状态
if (m_bCloseIng == true) return false;
if (m_wRountID != wRountID) return false;
if (m_dwRecvPacketCount == 0) return false;
if (IsValidSocket() == false) return false;
if (wDataSize > SocketModule.SOCKET_PACKAGE) return false;
//构造数据
byte\[\] cbDataBuffer = new byte\[SocketModule.SOCKET_BUFFER\];
CMD\_Head pHead = new CMD\_Head();
pHead.command.wMainCmdID = wMainCmdID;
pHead.command.wSubCmdID = wSubCmdID;
Array.Copy(pHead.Encode(), cbDataBuffer, CMD\_Head.sizeof\_CMD_Head);
if (wDataSize > 0)
{
Debug.Assert(pData != null);
Array.Copy(pData, 0, cbDataBuffer, CMD\_Head.sizeof\_CMD_Head, wDataSize);
}
//加密数据
short wSendSize = EncryptBuffer(cbDataBuffer, (short)(CMD\_Head.sizeof\_CMD_Head + wDataSize), (short)cbDataBuffer.GetLength(0));
//发送数据
return SendRawData(cbDataBuffer, wSendSize,wRountID);
}
//发送函数
public bool SendRawData(byte\[\] pData, short wDataSize, short wRountID) {
bool ret = true;
try
{
IAsyncResult asyncSend = m\_hSocket.BeginSend(pData, 0, wDataSize, SocketFlags.None, new AsyncCallback(sendCallback), m\_hSocket);
bool success = asyncSend.AsyncWaitHandle.WaitOne(5000, true);
if (!success)
{
CloseSocket(wRountID);
ret = false;
Console.WriteLine("Failed to SendMessage server.");
}
}
catch
{
ret = false;
Console.WriteLine("send message error");
}
return ret;
}
private void sendCallback(IAsyncResult async)
{
try
{
//结束挂起的异步发送
SocketError errorCode;
Socket socket = (Socket)async.AsyncState;
int dwSendSize = socket.EndSend(async, out errorCode);
Console.WriteLine("send size :" + dwSendSize.ToString());
}
catch (Exception e)
{
Console.WriteLine("error :" + e.ToString());
}
finally
{
}
}
ServerSocketItem和应用层的交互也是通过IServerSocketItemDelegate 一个ServerSocketItem对应一个GameUser实例 界面长这个样子,^^
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 xue_huashan@163.com
文章标题:Unity开发之网络一TCP服务端实现
文章字数:1.3k
本文作者:max-xue
发布时间:2018-07-03, 21:40:05
最后更新:2019-11-09, 22:39:39
原始链接:http://blog.le-more.com/2018/07/03/u3d/unity-e5-bc-80-e5-8f-91-e7/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。