上一篇写了如何进行TCP通信的套接字,这次来说一些怎么进行UDP的套接字通信,还用之前TCP套接字通信的那个示例页面,只是把后台的TCP通信改为UDP通信。首先我们先来修改SocketClient类
View Code
public class SocketClient { Socket _socket = null; static ManualResetEvent _clientDone = new ManualResetEvent(false); const int TIMEOUT_MILLISECONDS = 5000; const int MAX_BUFFER_SIZE = 2048; /// <summary> /// SocketClient构造函数 /// </summary> public SocketClient() { //创建一个具有一些属性的套接字 // AddressFamily.InterNetwork - 该套接字将使用IP版本4寻址方案来解决一个地址 // SocketType.Dgram - 一个套接字,支持数据(消息)包 // PrototcolType.Udp - 用户的数据协议(UDP) _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); } /// <summary> /// 使用已经建立的连接发送数据 /// </summary> /// <param name="serverName">服务器名称</param> /// <param name="portNumber">数据发送到的端口号</param> /// <param name="data">发送到服务器的数据</param> /// <returns>发送请求的结果</returns> public string Send(string serverName, int portNumber, string data) { string response = "Operation Timeout"; //我们用_socket对象初始化的连接方法 if (_socket != null) { // 创建一个SocketAsyncEventArgs对象 SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs(); //设置SocketAsyncEventArgs对象上下文的属性 socketEventArg.RemoteEndPoint = new DnsEndPoint(serverName, portNumber); // 内联事件处理程序完成事件。 // Note: This event handler was implemented inline in order to make this method self-contained. socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e) { response = e.SocketError.ToString(); //开启UI线程 _clientDone.Set(); }); //添加数据到缓冲数据 Add the data to be sent into the buffer byte[] payload = Encoding.UTF8.GetBytes(data); socketEventArg.SetBuffer(payload, 0, payload.Length); // Sets the state of the event to nonsignaled, causing threads to block //设置事件的接收状态,阻塞UI线程 _clientDone.Reset(); // Make an asynchronous Send request over the socket //让异步套接字发送请求 _socket.SendToAsync(socketEventArg); //这对时间内如果没有反应的话,阻塞UI线程最大时间为TIMEOUT_MILLISECONDS设定的毫秒数 _clientDone.WaitOne(TIMEOUT_MILLISECONDS); } else { response = "Socket is not initialized"; } return response; } /// <summary> /// Receive data from the server /// </summary> /// <param name="portNumber">The port on which to receive data</param> /// <returns>The data received from the server</returns> public string Receive(int portNumber) { string response = "Operation Timeout"; //我们正在建立一个套接字接收 if (_socket != null) { // 创建一个SocketAsyncEventArgs对象 SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs(); socketEventArg.RemoteEndPoint = new IPEndPoint(IPAddress.Any, portNumber); // 设置缓冲区接收数据 socketEventArg.SetBuffer(new Byte[MAX_BUFFER_SIZE], 0, MAX_BUFFER_SIZE); //内联事件处理程序完成事件。 // Note: This even handler was implemented inline in order to make this method self-contained. socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e) { if (e.SocketError == SocketError.Success) { //从缓冲区检索数据 response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred); response = response.Trim('\0'); } else { response = e.SocketError.ToString(); } _clientDone.Set(); }); _clientDone.Reset(); _socket.ReceiveFromAsync(socketEventArg); _clientDone.WaitOne(TIMEOUT_MILLISECONDS); } else { response = "Socket is not initialized"; } return response; } /// <summary> /// 关闭套接字连接并且释放所有相关资源 /// </summary> public void Close() { if (_socket != null) { _socket.Close(); } } }
对比可以看到,在UDP通信中SocketClient中之后Send和Receive两个方法,这是因为UDP不需要先进行与服务器的连接。在改好SocketClient类之后,我们来修改下页面中的两个单机事件btnEcho_Click和btnGetQuote_Click,其它方法保留
View Code
private void btnEcho_Click(object sender, RoutedEventArgs e) { //清空日志 ClearLog(); // Make sure we can perform this action with valid data //确保我们可以执行这个动作与数据的有效 if (ValidateRemoteHost() && ValidateInput()) { //实例化SocketClient SocketClient client = new SocketClient(); // Attempt to send our message to be echoed to the echo server //尝试发送消息响应服务器 Log(String.Format("Sending '{0}' to server ...", txtInput.Text), true); string result = client.Send(txtRemoteHost.Text, ECHO_PORT, txtInput.Text); Log(result, false); //接收来自服务器的响应 Log("Requesting Receive ...", true); result = client.Receive(ECHO_PORT); Log(result, false); //关闭套接字的连接 client.Close(); } } private void btnGetQuote_Click(object sender, RoutedEventArgs e) { // Clear the log ClearLog(); // Make sure we can perform this action with valid data if (ValidateRemoteHost()) { SocketClient client = new SocketClient(); Log(String.Format("Requesting a quote from server '{0}' ...", txtRemoteHost.Text), true); string dummyMessage = " "; string result = client.Send(txtRemoteHost.Text, QOTD_PORT, dummyMessage); Log(result, false); // Receive response from the QOTD server Log("Requesting Receive ...", true); result = client.Receive(QOTD_PORT); Log(result, false); // Close the socket connection explicitly client.Close(); } }
下面是触发btnEcho_Click和btnGetQuote_Click事件以后的截图