高性能异步Socket服务器(UDP)

采用异步模式设计的UDP服务器,源码如下:

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 }
29

 

posted @ 2010-02-05 10:33  yangli  阅读(4054)  评论(6编辑  收藏  举报