C# Socket UDP 案例 4 异步
View Code
1 using System; 2 using System.Net; 3 using System.Net.Sockets; 4 using System.ServiceProcess; 5 using System.Threading; 6 7 namespace TestUdpServer 8 { 9 // this class encapsulates a single packet that 10 // is either sent or received by a UDP socket 11 public class UDPPacketBuffer 12 { 13 // size of the buffer 14 public const int BUFFER_SIZE = 4096; 15 16 // the buffer itself 17 public byte[] Data; 18 19 // length of data to transmit 20 public int DataLength; 21 22 // the (IP)Endpoint of the remote host 23 public EndPoint RemoteEndPoint; 24 25 public UDPPacketBuffer() 26 { 27 this.Data = new byte[BUFFER_SIZE]; 28 29 // this will be filled in by the call to udpSocket.BeginReceiveFrom 30 RemoteEndPoint = (EndPoint)new IPEndPoint(IPAddress.Any, 0); 31 } 32 33 public UDPPacketBuffer(byte[] data, EndPoint remoteEndPoint) 34 { 35 this.Data = data; 36 this.DataLength = data.Length; 37 this.RemoteEndPoint = remoteEndPoint; 38 } 39 } 40 41 public abstract class UDPServer 42 { 43 // the port to listen on 44 private int udpPort; 45 46 // the UDP socket 47 private Socket udpSocket; 48 49 // the ReaderWriterLock is used solely for the purposes of shutdown (Stop()). 50 // since there are potentially many "reader" threads in the internal .NET IOCP 51 // thread pool, this is a cheaper synchronization primitive than using 52 // a Mutex object. This allows many UDP socket "reads" concurrently - when 53 // Stop() is called, it attempts to obtain a writer lock which will then 54 // wait until all outstanding operations are completed before shutting down. 55 // this avoids the problem of closing the socket with outstanding operations 56 // and trying to catch the inevitable ObjectDisposedException. 57 private ReaderWriterLock rwLock = new ReaderWriterLock(); 58 59 // number of outstanding operations. This is a reference count 60 // which we use to ensure that the threads exit cleanly. Note that 61 // we need this because the threads will potentially still need to process 62 // data even after the socket is closed. 63 private int rwOperationCount = 0; 64 65 // the all important shutdownFlag. This is synchronized through the ReaderWriterLock. 66 private bool shutdownFlag = true; 67 68 // these abstract methods must be implemented in a derived class to actually do 69 // something with the packets that are sent and received. 70 protected abstract void PacketReceived(UDPPacketBuffer buffer); 71 protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent); 72 73 // ServiceName 74 String ServiceName = "test"; 75 76 public UDPServer(int port) 77 { 78 this.udpPort = port; 79 } 80 81 public void Start() 82 { 83 if (shutdownFlag) 84 { 85 // create and bind the socket 86 IPEndPoint ipep = new IPEndPoint(IPAddress.Any, udpPort); 87 udpSocket = new Socket( 88 AddressFamily.InterNetwork, 89 SocketType.Dgram, 90 ProtocolType.Udp); 91 udpSocket.Bind(ipep); 92 // we're not shutting down, we're starting up 93 shutdownFlag = false; 94 95 // kick off an async receive. The Start() method will return, the 96 // actual receives will occur asynchronously and will be caught in 97 // AsyncEndRecieve(). 98 // I experimented with posting multiple AsyncBeginReceives() here in an attempt 99 // to "queue up" reads, however I found that it negatively impacted performance. 100 AsyncBeginReceive(); 101 } 102 } 103 104 protected void Stop() 105 { 106 if (!shutdownFlag) 107 { 108 // wait indefinitely for a writer lock. Once this is called, the .NET runtime 109 // will deny any more reader locks, in effect blocking all other send/receive 110 // threads. Once we have the lock, we set shutdownFlag to inform the other 111 // threads that the socket is closed. 112 rwLock.AcquireWriterLock(-1); 113 shutdownFlag = true; 114 udpSocket.Close(); 115 rwLock.ReleaseWriterLock(); 116 117 // wait for any pending operations to complete on other 118 // threads before exiting. 119 while (rwOperationCount > 0) 120 Thread.Sleep(1); 121 } 122 } 123 124 public bool IsRunning 125 { 126 // self-explanitory 127 get { return !shutdownFlag; } 128 } 129 130 private void AsyncBeginReceive() 131 { 132 // this method actually kicks off the async read on the socket. 133 // we aquire a reader lock here to ensure that no other thread 134 // is trying to set shutdownFlag and close the socket. 135 rwLock.AcquireReaderLock(-1); 136 137 if (!shutdownFlag) 138 { 139 // increment the count of pending operations 140 Interlocked.Increment(ref rwOperationCount); 141 142 // allocate a packet buffer 143 UDPPacketBuffer buf = new UDPPacketBuffer(); 144 145 try 146 { 147 // kick off an async read 148 udpSocket.BeginReceiveFrom( 149 buf.Data, 150 0, 151 UDPPacketBuffer.BUFFER_SIZE, 152 SocketFlags.None, 153 ref buf.RemoteEndPoint, 154 new AsyncCallback(AsyncEndReceive), 155 buf); 156 } 157 catch (SocketException se) 158 { 159 // something bad happened 160 System.Diagnostics.EventLog.WriteEntry(ServiceName, 161 "A SocketException occurred in UDPServer.AsyncBeginReceive():\n\n" + se.Message, 162 System.Diagnostics.EventLogEntryType.Error); 163 164 // an error occurred, therefore the operation is void. Decrement the reference count. 165 Interlocked.Decrement(ref rwOperationCount); 166 } 167 } 168 169 // we're done with the socket for now, release the reader lock. 170 rwLock.ReleaseReaderLock(); 171 } 172 173 private void AsyncEndReceive(IAsyncResult iar) 174 { 175 // Asynchronous receive operations will complete here through the call 176 // to AsyncBeginReceive 177 178 // aquire a reader lock 179 rwLock.AcquireReaderLock(-1); 180 181 if (!shutdownFlag) 182 { 183 // start another receive - this keeps the server going! 184 AsyncBeginReceive(); 185 186 // get the buffer that was created in AsyncBeginReceive 187 // this is the received data 188 UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; 189 190 try 191 { 192 // get the length of data actually read from the socket, store it with the 193 // buffer 194 buffer.DataLength = udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); 195 196 // this operation is now complete, decrement the reference count 197 Interlocked.Decrement(ref rwOperationCount); 198 199 // we're done with the socket, release the reader lock 200 rwLock.ReleaseReaderLock(); 201 202 // call the abstract method PacketReceived(), passing the buffer that 203 // has just been filled from the socket read. 204 PacketReceived(buffer); 205 } 206 catch (SocketException se) 207 { 208 // something bad happened 209 System.Diagnostics.EventLog.WriteEntry(ServiceName, 210 "A SocketException occurred in UDPServer.AsyncEndReceive():\n\n" + se.Message, 211 System.Diagnostics.EventLogEntryType.Error); 212 213 // an error occurred, therefore the operation is void. Decrement the reference count. 214 Interlocked.Decrement(ref rwOperationCount); 215 216 // we're done with the socket for now, release the reader lock. 217 rwLock.ReleaseReaderLock(); 218 } 219 } 220 else 221 { 222 // nothing bad happened, but we are done with the operation 223 // decrement the reference count and release the reader lock 224 Interlocked.Decrement(ref rwOperationCount); 225 rwLock.ReleaseReaderLock(); 226 } 227 } 228 229 public void AsyncBeginSend(UDPPacketBuffer buf) 230 { 231 // by now you should you get the idea - no further explanation necessary 232 233 rwLock.AcquireReaderLock(-1); 234 235 if (!shutdownFlag) 236 { 237 try 238 { 239 Interlocked.Increment(ref rwOperationCount); 240 udpSocket.BeginSendTo( 241 buf.Data, 242 0, 243 buf.DataLength, 244 SocketFlags.None, 245 buf.RemoteEndPoint, 246 new AsyncCallback(AsyncEndSend), 247 buf); 248 } 249 catch (SocketException se) 250 { 251 System.Diagnostics.EventLog.WriteEntry(ServiceName, 252 "A SocketException occurred in UDPServer.AsyncBeginSend():\n\n" + se.Message, 253 System.Diagnostics.EventLogEntryType.Error); 254 } 255 } 256 257 rwLock.ReleaseReaderLock(); 258 } 259 260 private void AsyncEndSend(IAsyncResult iar) 261 { 262 // by now you should you get the idea - no further explanation necessary 263 264 rwLock.AcquireReaderLock(-1); 265 266 if (!shutdownFlag) 267 { 268 UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; 269 270 try 271 { 272 int bytesSent = udpSocket.EndSendTo(iar); 273 274 // note that call to the abstract PacketSent() method - we are passing the number 275 // of bytes sent in a separate parameter, since we can't use buffer.DataLength which 276 // is the number of bytes to send (or bytes received depending upon whether this 277 // buffer was part of a send or a receive). 278 PacketSent(buffer, bytesSent); 279 } 280 catch (SocketException se) 281 { 282 System.Diagnostics.EventLog.WriteEntry(ServiceName, 283 "A SocketException occurred in UDPServer.AsyncEndSend():\n\n" + se.Message, 284 System.Diagnostics.EventLogEntryType.Error); 285 } 286 } 287 288 Interlocked.Decrement(ref rwOperationCount); 289 rwLock.ReleaseReaderLock(); 290 } 291 292 } 293 } 实际使用时需继承抽象类UDPServer,并实现异步处理数据的相关方案,示例如下: 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Runtime.InteropServices; 5 6 namespace TestUdpServer 7 { 8 public class TUdpServer : UDPServer 9 { 10 11 public TUdpServer(int port) 12 : base(port) 13 { 14 Console.WriteLine("Server Start...@" + port.ToString()); 15 } 16 17 protected override void PacketReceived(UDPPacketBuffer buffer) 18 { 19 AsyncBeginSend(buffer); 20 } 21 22 protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent) 23 { 24 25 } 26 27 } 28 }
摘自:http://www.diybl.com/course/3_program/cshapo/csharpjs/20100714/442092.html