SocketAsyncEventArgs是一個(gè)套接字操作的類(lèi),,主要作用是實(shí)現(xiàn)socket消息的異步接收和發(fā)送,跟Socket的BeginSend和 先說(shuō)說(shuō)SockeAsyncEventArgs類(lèi)的操作方法,,以下是摘自MSDN的內(nèi)容(MSDN的SockeAsyncEventArgs類(lèi)描述): 1,、分配一個(gè)新的 SocketAsyncEventArgs 上下文對(duì)象,或者從應(yīng)用程序池中獲取一個(gè)空閑的此類(lèi)對(duì)象,。 1 SocketAsyncEventArgs saea = new SocketAsyncEventArgs(); 2 //或者(這里的SocketAsyncEventArgsPool類(lèi)一般是自己實(shí)現(xiàn),,MSDN有通過(guò)棧結(jié)構(gòu)實(shí)現(xiàn)的程序池,也可以使用隊(duì)列或鏈表): 3 SocketAsyncEventArgs saea = new SocketAsyncEventArgsPool().Pop();
2,、將該上下文對(duì)象的屬性設(shè)置為要執(zhí)行的操作(例如,,完成回調(diào)方法、數(shù)據(jù)緩沖區(qū),、緩沖區(qū)偏移量以及要傳輸?shù)淖畲髷?shù)據(jù)量),。SocketAsyncEventArgs一般會(huì)根據(jù)操作執(zhí)行相同的回調(diào)函數(shù),所有設(shè)置內(nèi)容在不同操作的回調(diào)都可以訪問(wèn),,我在調(diào)試時(shí)發(fā)現(xiàn)不能同時(shí)收發(fā)消息(可能是半雙工),,因此使用接收和發(fā)送試用兩個(gè)對(duì)象 1 byte[] buffer = new byte[1024]; 2 saea.SetBuffer(buffer, 0, buffer.Length); //設(shè)置緩沖區(qū) 3 saea.Completed += new EventHandler<SocketAsyncEventArgs>(MethodName); //設(shè)置回調(diào)方法 4 saea.RemoteEndPoint = new IPEndPoint(IPAddress.Any,1234); //設(shè)置遠(yuǎn)端連接節(jié)點(diǎn),一般用于接收消息 5 saea.UserToken = new AsyncUserToken(); //設(shè)置用戶(hù)信息,,一般把連接的Socket對(duì)象放在這里
3,、調(diào)用適當(dāng)?shù)奶捉幼址椒?(xxxAsync) 以啟動(dòng)異步操作。 1 //這是調(diào)用套接字的方法,,即socket調(diào)用的方法: 2 Socket socket = saea.AcceptSocket; 3 socket.ConnectAsync(saea); //異步進(jìn)行連接 4 socket.AcceptAsync(saea); //異步接收連接 5 socket.ReceiveAsync(saea); //異步接收消息 6 socket.SendAsync(saea); //異步發(fā)送消息 7 //這里注意的是,每個(gè)操作方法返回的是布爾值,,這個(gè)布爾值的作用,,是表明當(dāng)前操作是否有等待I/O的情況,如果返回false則表示當(dāng)前是同步操作,,不需要等待,,此時(shí)要要同步執(zhí)行回調(diào)方法,一般寫(xiě)法是 8 bool willRaiseEvent = socket.ReceiveAsync(saea); //繼續(xù)異步接收消息 9 if (!willRaiseEvent) 10 { 11 MethodName(saea); 12 }
6,、將該上下文重用于另一個(gè)操作,,將它放回到應(yīng)用程序池中,或者將它丟棄。 1 saea.AcceptSocket = null;//重新監(jiān)聽(tīng) 2 socket.ReceiveAsync(saea);//重新接收 3 socket.SendAsync(saea);//重新發(fā)送
關(guān)于具體的實(shí)現(xiàn),,類(lèi)似于之前我記下的簡(jiǎn)單Socket通信,先是SocketServerManager的實(shí)現(xiàn),,該實(shí)現(xiàn)也是參考自MSDN: 1 public class SocketServerManager 2 { 3 readonly Socket _socket; //監(jiān)聽(tīng)Socket 4 readonly EndPoint _endPoint; 5 private const int Backlog = 100; //允許連接數(shù)目 6 private int byteSize = 64; 7 8 //同時(shí)UI界處理的事件 9 public delegate void OnEventCompletedHanlder(MessageFormat msg); 10 public event OnEventCompletedHanlder OnReceiveCompletedEvent; 11 public event OnEventCompletedHanlder OnSendCompletedEvent; 12 public event OnEventCompletedHanlder OnConnectedEvent; 13 public event OnEventCompletedHanlder OnDisconnectEvent; 14 public event OnEventCompletedHanlder OnNotConnectEvent; 15 16 //private BufferManager bufferManager; //消息緩存管理 17 SocketAsyncEventArgsPool rwPool; //SAEA池 18 private Semaphore maxClient; 19 private Dictionary<string, SocketAsyncEventArgs> dicSAEA = null; 20 21 public SocketServerManager(string ip, int port) 22 { 23 _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 24 IPAddress ipAddress = IPAddress.Parse(ip); 25 _endPoint = new IPEndPoint(ipAddress, port); 26 //bufferManager = new BufferManager(totalBytes, byteSize); 27 maxClient = new Semaphore(Backlog, Backlog); 28 Init(); 29 } 30 31 public void Init() 32 { 33 //bufferManager.InitBuffer(); 34 SocketAsyncEventArgs rwEventArgs; 35 rwPool = new SocketAsyncEventArgsPool(Backlog); 36 dicSAEA = new Dictionary<string, SocketAsyncEventArgs>(); 37 for (int i = 0; i < 100; i++) 38 { 39 rwEventArgs = new SocketAsyncEventArgs(); 40 rwEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed); 41 rwEventArgs.UserToken = new AsyncUserToken(); 42 43 rwEventArgs.SetBuffer(new byte[byteSize],0,byteSize); 44 //bufferManager.SetBuffer(rwEventArgs); 45 46 rwPool.Push(rwEventArgs); 47 } 48 } 49 50 /// <summary> 51 /// 開(kāi)啟Socket監(jiān)聽(tīng) 52 /// </summary> 53 public void Start() 54 { 55 _socket.Bind(_endPoint); //綁定本地地址進(jìn)行監(jiān)聽(tīng) 56 _socket.Listen(Backlog); //設(shè)置監(jiān)聽(tīng)數(shù)量 57 58 StartAccept(null); 59 } 60 61 public void StartAccept(SocketAsyncEventArgs acceptEventArg) 62 { 63 if (acceptEventArg == null) 64 { 65 acceptEventArg = new SocketAsyncEventArgs(); 66 acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnectedCompleted); 67 } 68 else 69 { 70 acceptEventArg.AcceptSocket = null; 71 } 72 73 maxClient.WaitOne(); 74 bool willRaiseEvent = _socket.AcceptAsync(acceptEventArg); 75 if (!willRaiseEvent) 76 { 77 ProcessAccept(acceptEventArg); 78 } 79 } 80 81 private void ProcessAccept(SocketAsyncEventArgs e) 82 { 83 if (e.SocketError != SocketError.Success) return; //異步處理失敗,,不做處理 84 SocketAsyncEventArgs saea = rwPool.Pop(); 85 AsyncUserToken token = saea.UserToken as AsyncUserToken; 86 token.UserSocket = e.AcceptSocket; //獲取遠(yuǎn)端對(duì)話Socket對(duì)象 87 string ipRemote = token.UserSocket.RemoteEndPoint.ToString(); 88 string ip = token.UserSocket.RemoteEndPoint.ToString(); 89 MessageFormat msg = new MessageFormat(string.Format("遠(yuǎn)程地址[{0}]成功連接到本地", ipRemote), ip, MsgType.Empty); 90 msg.tag = MsgType.Empty; 91 if (OnConnectedEvent != null) OnConnectedEvent(msg); //調(diào)用UI方法處理 92 93 //連接成功后,發(fā)送消息通知遠(yuǎn)程客戶(hù)端 94 //OnSend("Connected Success !", _sendSocket.RemoteEndPoint); 95 SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs(); 96 sendArgs.RemoteEndPoint = token.UserSocket.RemoteEndPoint; 97 sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed); 98 sendArgs.UserToken = saea.UserToken; 99 dicSAEA.Add(token.UserSocket.RemoteEndPoint.ToString(), sendArgs); 100 bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(saea); 101 if (!willRaiseEvent) 102 { 103 OnReceiveCompleted(saea); 104 } 105 106 StartAccept(e); 107 } 108 109 /// <summary> 110 /// 遠(yuǎn)端地址連接本地成功的回調(diào) 111 /// </summary> 112 /// <param name="sender"></param> 113 /// <param name="e"></param> 114 public void OnConnectedCompleted(object sender, SocketAsyncEventArgs e) 115 { 116 ProcessAccept(e); 117 } 118 119 public void IO_Completed(object sender, SocketAsyncEventArgs e) 120 { 121 switch (e.LastOperation) 122 { 123 case SocketAsyncOperation.Receive: 124 OnReceiveCompleted(e); 125 break; 126 case SocketAsyncOperation.Send: 127 OnSendCompleted(e); 128 break; 129 default: 130 throw new ArgumentException("The last operation completed on the socket was not a receive or send"); 131 } 132 } 133 134 /// <summary> 135 /// 執(zhí)行異步發(fā)送消息 136 /// </summary> 137 /// <param name="msg">消息內(nèi)容</param> 138 /// <param name="ip">發(fā)送遠(yuǎn)端地址</param> 139 public void OnSend(MessageFormat mf) 140 { 141 if (!dicSAEA.ContainsKey(mf.ipStr)) 142 { 143 if (OnNotConnectEvent != null) 144 { 145 OnNotConnectEvent(new MessageFormat("不存在此連接客戶(hù)端","",MsgType.Empty)); 146 return; 147 } 148 } 149 SocketAsyncEventArgs saea = dicSAEA[mf.ipStr]; 150 AsyncUserToken token = saea.UserToken as AsyncUserToken; 151 if (saea == null) return; 152 //saea.SetBuffer(sendBuffer, 0, sendBuffer.Length); //設(shè)置SAEA的buffer消息內(nèi)容 153 byte[] sendBuffer = Encoding.Unicode.GetBytes(string.Format("[length={0}]{1}", mf.msgStr.Length, mf.msgStr)); 154 saea.SetBuffer(sendBuffer, 0, sendBuffer.Length); 155 bool willRaiseEvent = token.UserSocket.SendAsync(saea); 156 if (!willRaiseEvent) 157 { 158 OnSendCompleted(saea); 159 } 160 } 161 162 /// <summary> 163 /// 發(fā)送消息回調(diào)處理 164 /// </summary> 165 /// <param name="sender"></param> 166 /// <param name="e"></param> 167 public void OnSendCompleted(SocketAsyncEventArgs e) 168 { 169 AsyncUserToken token = e.UserToken as AsyncUserToken; 170 byte[] sendBuffer = e.Buffer; 171 string msgStr = Encoding.Unicode.GetString(sendBuffer); 172 string ipAddress = token.UserSocket.RemoteEndPoint.ToString(); 173 MessageFormat msg = new MessageFormat(msgStr, ipAddress, MsgType.Send); 174 if (OnSendCompletedEvent != null) OnSendCompletedEvent(msg); //調(diào)用UI方法處理 175 } 176 177 /// <summary> 178 /// 接收消息回調(diào)處理 179 /// </summary> 180 /// <param name="e"></param> 181 public void OnReceiveCompleted(SocketAsyncEventArgs e) 182 { 183 if (e.SocketError != SocketError.Success) return; //判斷消息的接收狀態(tài) 184 AsyncUserToken token = e.UserToken as AsyncUserToken; 185 int lengthBuffer = e.BytesTransferred; //獲取接收的字節(jié)長(zhǎng)度 186 string ipAddress = token.UserSocket.RemoteEndPoint.ToString(); 187 MessageFormat msg = new MessageFormat(); 188 //如果接收的字節(jié)長(zhǎng)度為0,,則判斷遠(yuǎn)端服務(wù)器關(guān)閉連接 189 if (lengthBuffer <= 0) 190 { 191 msg.msgStr = "遠(yuǎn)端服務(wù)器已經(jīng)斷開(kāi)連接"; 192 msg.ipStr = ipAddress; 193 msg.tag = MsgType.Handler; 194 if (OnDisconnectEvent != null) OnDisconnectEvent(msg); 195 CloseClientSocket(e); 196 } 197 else 198 { 199 byte[] receiveBuffer = e.Buffer; 200 byte[] buffer = new byte[lengthBuffer]; 201 Buffer.BlockCopy(receiveBuffer, 0, buffer, 0, lengthBuffer); 202 msg.msgStr = Encoding.Unicode.GetString(buffer); 203 msg.ipStr = ipAddress; 204 msg.tag = MsgType.Receive; 205 bool willRaiseEvent = token.UserSocket.ReceiveAsync(e); //繼續(xù)異步接收消息 206 if (!willRaiseEvent) 207 { 208 OnReceiveCompleted(e); 209 } 210 if (OnReceiveCompletedEvent != null) OnReceiveCompletedEvent(msg); //調(diào)用UI方法處理 211 } 212 } 213 214 private void CloseClientSocket(SocketAsyncEventArgs e) 215 { 216 AsyncUserToken token = e.UserToken as AsyncUserToken; 217 try 218 { 219 token.UserSocket.Shutdown(SocketShutdown.Send); 220 } 221 catch (Exception) { } 222 dicSAEA.Remove(token.UserSocket.RemoteEndPoint.ToString()); 223 token.UserSocket.Close(); 224 225 maxClient.Release(); 226 rwPool.Push(e); 227 } 228 }
通過(guò)一個(gè)棧結(jié)構(gòu)SocketAsyncEventArgsPool保存的SocketAsyncEventArgs對(duì)象是用于接收消息,,為了做到雙方通訊,我每次在接收到遠(yuǎn)端客戶(hù)端連接時(shí),,就創(chuàng)建一個(gè)新的SocketAsyncEventArgs對(duì)象保存在Dictionary結(jié)構(gòu)中,,這樣在消息發(fā)送是就可以根據(jù)Ip來(lái)發(fā)送給遠(yuǎn)程客戶(hù)端。
客戶(hù)端的實(shí)現(xiàn)比較簡(jiǎn)單,,不用考慮多方的通訊: 1 public class SocketClientManager 2 { 3 readonly Socket _socket; //用于消息交互的socket對(duì)象 4 readonly EndPoint _endPoint; //遠(yuǎn)端地址 5 readonly SocketAsyncEventArgs _saea; //處理連接和接收SAEA對(duì)象 6 //處理發(fā)送的SAEA處理,,由于綁定不同的回調(diào)函數(shù),因此需要不同的SAEA對(duì)象 7 SocketAsyncEventArgs _sendSaea; 8 9 //處理UI的事件 10 public delegate void OnEventCompletedHanlder(MessageFormat msgFormat); 11 public event OnEventCompletedHanlder OnConnectedEvent; //連接成功事件 12 public event OnEventCompletedHanlder OnReceiveCompletedEvent; //收到消息事件 13 public event OnEventCompletedHanlder OnSendCompletedEvent; //發(fā)送成功事件 14 15 public SocketClientManager(string ip, int port) 16 { 17 _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 18 IPAddress ipAddress = IPAddress.Parse(ip); 19 _endPoint = new IPEndPoint(ipAddress, port); 20 _saea = new SocketAsyncEventArgs {RemoteEndPoint = _endPoint}; 21 } 22 23 /// <summary> 24 /// 開(kāi)啟進(jìn)行遠(yuǎn)程連接 25 /// </summary> 26 public void Start() 27 { 28 _saea.Completed += OnConnectedCompleted; 29 _socket.ConnectAsync(_saea); //進(jìn)行異步連接 30 31 } 32 33 /// <summary> 34 /// 連接成功的事件回調(diào)函數(shù) 35 /// </summary> 36 /// <param name="sender"></param> 37 /// <param name="e"></param> 38 public void OnConnectedCompleted(object sender, SocketAsyncEventArgs e) 39 { 40 if (e.SocketError != SocketError.Success) return; 41 Socket socket = sender as Socket; 42 string ipRemote = socket.RemoteEndPoint.ToString(); 43 MessageFormat messageFormat = new MessageFormat(string.Format("連接服務(wù)器[{0}]成功,!", ipRemote), socket.LocalEndPoint.ToString(), MsgType.Empty); 44 if (OnConnectedEvent != null) OnConnectedEvent(messageFormat); 45 46 //開(kāi)啟新的接受消息異步操作事件 47 var receiveSaea = new SocketAsyncEventArgs(); 48 var receiveBuffer = new byte[1024 * 4]; 49 receiveSaea.SetBuffer(receiveBuffer, 0, receiveBuffer.Length); //設(shè)置消息的緩沖區(qū)大小 50 receiveSaea.Completed += OnReceiveCompleted; //綁定回調(diào)事件 51 receiveSaea.RemoteEndPoint = _endPoint; 52 _socket.ReceiveAsync(receiveSaea); 53 } 54 55 /// <summary> 56 /// 接受消息的回調(diào)函數(shù) 57 /// </summary> 58 /// <param name="sender"></param> 59 /// <param name="e"></param> 60 public void OnReceiveCompleted(object sender, SocketAsyncEventArgs e) 61 { 62 if (e.SocketError == SocketError.OperationAborted) return; 63 var socket = sender as Socket; 64 MessageFormat messageFormat = new MessageFormat(); 65 if (e.SocketError == SocketError.Success &&e.BytesTransferred > 0) 66 { 67 string ipAddress = socket.RemoteEndPoint.ToString(); 68 int lengthBuffer = e.BytesTransferred; 69 byte[] receiveBuffer = e.Buffer; 70 byte[] buffer = new byte[lengthBuffer]; 71 Buffer.BlockCopy(receiveBuffer, 0, buffer, 0, lengthBuffer); 72 messageFormat.msgStr = Encoding.Unicode.GetString(buffer); 73 messageFormat.ipStr = ipAddress; 74 messageFormat.tag = MsgType.Receive;; 75 socket.ReceiveAsync(e); 76 } 77 else if (e.SocketError == SocketError.ConnectionReset && e.BytesTransferred == 0) 78 { 79 messageFormat.msgStr = "服務(wù)器已經(jīng)斷開(kāi)連接"; 80 messageFormat.ipStr = socket.RemoteEndPoint.ToString(); 81 messageFormat.tag = MsgType.Handler; 82 } 83 else 84 { 85 return; 86 } 87 if (OnReceiveCompletedEvent != null) OnReceiveCompletedEvent(messageFormat); 88 } 89 90 /// <summary> 91 /// 發(fā)送消息回調(diào)函數(shù) 92 /// </summary> 93 /// <param name="sender"></param> 94 /// <param name="e"></param> 95 public void OnSendCompleted(object sender, SocketAsyncEventArgs e) 96 { 97 if (e.SocketError != SocketError.Success) return; 98 var socket = sender as Socket; 99 byte[] sendBuffer = e.Buffer; 100 MessageFormat messageFormat = new MessageFormat(Encoding.Unicode.GetString(sendBuffer),socket.RemoteEndPoint.ToString(),MsgType.Send); 101 if (OnSendCompletedEvent != null) OnSendCompletedEvent(messageFormat); 102 } 103 104 /// <summary> 105 /// 斷開(kāi)連接 106 /// </summary> 107 public void OnDisConnect() 108 { 109 if (_socket != null) 110 { 111 try 112 { 113 _socket.Shutdown(SocketShutdown.Both); 114 } 115 catch (SocketException ex) 116 { 117 } 118 finally 119 { 120 _socket.Close(); 121 } 122 } 123 } 124 125 /// <summary> 126 /// 發(fā)送消息 127 /// </summary> 128 /// <param name="msg"></param> 129 public void SendMsg(MessageFormat mf) 130 { 131 byte[] sendBuffer = Encoding.Unicode.GetBytes(string.Format("[length={0}]{1}", mf.msgStr.Length, mf.msgStr)); 132 if (_sendSaea == null) 133 { 134 _sendSaea = new SocketAsyncEventArgs {RemoteEndPoint = _endPoint}; 135 _sendSaea.Completed += OnSendCompleted; 136 } 137 _sendSaea.SetBuffer(sendBuffer, 0, sendBuffer.Length); 138 if (_socket != null) _socket.SendAsync(_sendSaea); 139 } 140 }
同樣是使用收發(fā)不同的SocketAsyncEventArgs對(duì)象,。
另外,關(guān)于解決緩存容量不足以容納一條消息的半包問(wèn)題,,這里使用了簡(jiǎn)單的字符處理類(lèi),,這個(gè)類(lèi)是復(fù)制自Jimmy Zhang的類(lèi)RequestHandler,當(dāng)然,,方法還是不完善的,,難以解決不同順序的消息接受。
|
|