客户端发送消息的时候,也是异步发送,所以和服务器直接发送不一样,需要先将bytes数据都包装在ByteArray
里。
在ByteArray
内添加新的构造函数
1 2 3 4 5 6 7 8
| public ByteArray(byte[] defaultBytes) { Bytes = defaultBytes; Capacity = defaultBytes.Length; m_InitSize = defaultBytes.Length; ReadIdx = 0; WriteIdx = defaultBytes.Length; }
|
Capacity = defaultBytes.Length; WriteIdx = defaultBytes.Length;
表示构造的ByteArray
并不是空的,是一个可读不可写的状态,因为此时它的Remain(Capacity - WriteIdx)
等于0
在客户端NetManager
中声明一个Queue,把需要发送的ByteArray
都放在这个Queue里,然后将Queue中的ByteArray
一个一个发送出去。这个发送的过程是异步的。所以每次对于这个Queue的操作都要加锁。并且这个异步的发送还是递归的,会把Queue的消息都发送完。
1 2 3 4 5 6 7 8 9 10
| private Queue<ByteArray> m_WriteQueue;
void InitState() { m_WriteQueue = new Queue<ByteArray>(); }
|
SendMessage
在NetManager
中添加SendMessage
方法,基本上和服务器差不多,但是多了添加到队列的操作和回调方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public void SendMessage(MsgBase msg) { if(m_Socket == null && !m_Socket.Connected) { return; } if(m_IsConnecting) { Debug.LogError("正在连接服务器,无法发送消息"); return; } if(m_IsClosing) { Debug.LogError("正在关闭连接中,无法发送消息"); return; } try { byte[] nameBytes = MsgBase.EncodeName(msg); byte[] bodyBytes = MsgBase.Encode(msg); int len = nameBytes.Length + bodyBytes.Length; byte[] headBytes = BitConverter.GetBytes(len); byte[] sendBytes = new byte[len + headBytes.Length]; Array.Copy(headBytes,0,sendBytes, 0, headBytes.Length); Array.Copy(nameBytes,0,sendBytes,headBytes.Length, nameBytes.Length); Array.Copy(bodyBytes,0,sendBytes,headBytes.Length + nameBytes.Length, bodyBytes.Length); ByteArray byteArray = new ByteArray(sendBytes); int count = 0; lock(m_WriteQueue) { m_WriteQueue.Enqueue(byteArray); count = m_WriteQueue.Count; } if(count == 1) { m_Socket.BeginSend(sendBytes, 0, sendBytes.Length,SocketFlags.None,SendCallback,m_Socket); } } catch (Exception ex) { Debug.LogError("SendMessage Error:" + ex); CloseConnection(); } }
|
修改CloseConnection
之前我们标注过CloseConnection
还没有写完,需要判断消息的发送队列,在这里补完这部分的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
public void CloseConnection(bool normal = true) { if(m_Socket == null || m_IsConnecting) return;
if(m_IsConnecting) { return; } if (m_WriteQueue.Count > 0) { m_IsClosing = true; } else { ReallyClose(normal); } } void ReallyClose(bool normal = true) { SecretKey = ""; m_Socket.Close(); FirstEvent(NetEvent.Close, normal.ToString()); if (m_HeartThread != null && m_HeartThread.IsAlive) { m_HeartThread.Abort(); m_HeartThread = null; } if (m_MsgThread != null && m_MsgThread.IsAlive) { m_MsgThread.Abort(); m_MsgThread = null; } Debug.Log("CloseSocket"); }
|
在NetManager
中,有多处都会调用CloseConnection
方法,接收数据异常了会调用,服务器没有消息了也会调用……,每次调用时,都会先判断当先的消息发送队列m_WriteQueue
是否还有消息,如果还有,把m_IsClosing
设为true,让SendMessage
的回调来彻底关闭Socket。所以我们分离出来了ReallyClose
方法来彻底关闭
SendCallback回调
在NetManager
中添加SendCallback
回调方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
void SendCallback(IAsyncResult ar) { try { Socket socket = (Socket)ar.AsyncState; if (socket == null || !socket.Connected) return; int count = socket.EndSend(ar); ByteArray ba; lock(m_WriteQueue) { ba = m_WriteQueue.Peek(); } ba.ReadIdx += count; if(ba.Length == 0) { lock(m_WriteQueue) { m_WriteQueue.Dequeue(); if(m_WriteQueue.Count > 0) { ba = m_WriteQueue.Peek(); } else { ba = null; } } } if(ba != null) { socket.BeginSend(ba.Bytes, ba.ReadIdx, ba.Length, SocketFlags.None, SendCallback, socket); } else if (m_IsClosing) { ReallyClose(); } } catch (SocketException ex) { Debug.LogError("SendCallback Error: " + ex.ToString()); CloseConnection(); } }
|
在回调方法中,使用socket.EndSend(ar);
来获取上一次发送的byte数量,来判断是否完整发送了消息。在回调中发现没有消息可以发了并且m_IsClosing
已经为true,说明此时需要关闭链接,调用ReallyClose
方法。