代码改变世界

C# Socket学习笔记二

2014-11-17 18:55  wuzhang  阅读(1527)  评论(2编辑  收藏  举报

  小记:昨天咱们已经了解了Socket的通信原理,可是点对点的一次通信并不是我们想要的,那么今天那我们就继续学习异步通信,简单来说就是服务器端和客户端可以进行多次 互发信息的通信而不用担心通道会关闭。在介绍异步通信时,客户端和服务器端的连接和上面介绍的同步通信建立连接的方式是一样的,只是接收和发送数据的方式改变了!

1.什么是异步通信?

异步:客户端请求之后,不必等到服务器回应之后就可以发送下一条请求,并行运行。

2.同步与异步的区别?

同步:我叫你吃饭,你若暂时有事情我就一直在那等,不干别的事情,直到你忙完。
异步:我叫你吃饭,你若暂时有事情,我就先撤,干点别的,你忙完了再通过某种方式,如电话,通知我。

异步通信的好处我理解的就是可以多次使用一个Socket增加网络的吞吐量,提高资源的利用率。

 

下面开始代码分析,和上篇的没多大差别,就是发送和接收数据的方式变了:

服务器端:TcpServer.cs

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 using System.Net;
  7 using System.Net.Sockets;
  8 using System.Threading;
  9 
 10 
 11 namespace TcpServer
 12 {
 13     class Program
 14     {
 15         public static Socket serverSocket;
 16         public static Thread thread;
 17 
 18         static void Main(string[] args)
 19         {
 20             //确定端口号
 21             int port = 6000;
 22 
 23             //设定连接IP
 24             string host = "127.0.0.1";
 25 
 26             //将IP地址字符串转化为IP地址实例
 27             IPAddress ip = IPAddress.Parse(host);
 28 
 29             //将网络端点表示为 IP 地址和端口号
 30             IPEndPoint ipe = new IPEndPoint(ip, port);
 31 
 32             //建立Socket 
 33             //addressFamily 参数一指定 Socket 类使用的寻址方案
 34             //socketType    参数二指定 Socket 类的类型
 35             //protocolType  参数三指定 Socket 使用的协议。 
 36             Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 37 
 38 
 39             //socket与本地终结点建立关联
 40             socket.Bind(ipe);
 41             string strSend = "HelloClient";
 42             //开始监听端口
 43             socket.Listen(10);
 44             Console.WriteLine("服务端已开启,等待客户端连接.....\t" + DateTime.Now.ToString() + DateTime.Now.Millisecond.ToString());
 45 
 46             //为新建的连接建立新的Socket目的为客户端将要建立连接
 47             serverSocket = socket.Accept();
 48             Console.WriteLine("连接已建立......\t\t" + DateTime.Now.ToString() + DateTime.Now.Millisecond.ToString());
 49             Console.WriteLine("客户端->服务端:\t" + serverSocket.RemoteEndPoint + "->" + serverSocket.LocalEndPoint);
 50             
 51             //while (true)
 52             {
 53                 string recStr =string.Empty;
 54                 //定义缓冲区用于接收客户端的数据
 55                 byte[] recbyte = new byte[1024];
 56             
 57                 //开始接收数据
 58                 ReceiveData();
 59                 
 60                 //服务端给客户端回送消息
 61                 strSend = "HelloClient";
 62 
 63                 //服务端发送数据
 64                 //strSend = Console.ReadLine();
 65                 Send(strSend);
 66 
 67                 //开启线程监视客户端是否断开
 68                 thread = new Thread(ClientConnectOrNot);
 69                 Thread.Sleep(0);
 70                 thread.Start();
 71           
 72 
 73                 //serverSocket.Close();
 74                 Console.ReadLine();
 75             }
 76         }
 77 
 78         /// <summary>
 79         /// 监视客户端是否断开
 80         /// </summary>
 81         public static void ClientConnectOrNot()
 82         {
 83             //Console.WriteLine(serverSocket.Poll(-1, SelectMode.SelectRead));
 84             if (serverSocket.Poll(-1, SelectMode.SelectRead) == true)//客户端断开
 85             {
 86                 Console.WriteLine(serverSocket.RemoteEndPoint + "断开了连接");
 87                 thread.Abort();
 88 
 89             }
 90             else
 91             {
 92                 Console.WriteLine(thread.ThreadState);
 93             }
 94         }
 95         #region
 96         /// <summary>
 97         /// 异步连接
 98         /// </summary>
 99         /// <param name="ip"></param>
100         /// <param name="port"></param>
101         /// <param name="clientSocket"></param>
102         public static void Connect(IPAddress ip, int port)
103         {
104             serverSocket.BeginConnect(ip, port, new AsyncCallback(ConnectCallback), serverSocket);
105         }
106 
107         private static void ConnectCallback(IAsyncResult ar)
108         {
109             try
110             {
111                 Socket handler = (Socket)ar.AsyncState;
112                 handler.EndConnect(ar);
113             }
114             catch (SocketException ex)
115             {
116                 throw ex;
117             }
118         }
119         /// <summary>
120         /// 发送数据
121         /// </summary>
122         /// <param name="data"></param>
123         public static void Send(string data)
124         {
125             //Send(System.Text.Encoding.UTF8.GetBytes(data));
126             //解决中文乱码
127             Send(UTF8Encoding.UTF8.GetBytes(data));
128 
129             //byte[] byteData = UTF8Encoding.UTF8.GetBytes(data);
130             //Send(Encoding.ASCII.GetBytes(data));
131         }
132         /// <summary>
133         /// 发送数据
134         /// </summary>
135         /// <param name="byteData"></param>
136         private static void Send(byte[] byteData)
137         {
138             try
139             {
140                 int length = byteData.Length;
141                 //byte[] head = BitConverter.GetBytes(length);               //这个为什么不对,我也不清楚
142                 byte[] head = Encoding.ASCII.GetBytes(length.ToString());    //将发送数据的字节长度写到报头中
143                 //head.Length = 4;                                           //开始我想指定头部4个字节长度用来存放字节的总数便于拆包
144                 byte[] data = new byte[head.Length + byteData.Length];       //发送包的总大小
145                 for (int i = 0; i < 3; i++)
146                 {
147                     data[i] = 0;
148                 }
149                 Array.Copy(head, data, head.Length);                         //将head复制到data中
150                 Array.Copy(byteData, 0, data, head.Length, byteData.Length); //将byteData复制到data中
151                 
152                 serverSocket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), serverSocket);
153             }
154             catch (SocketException ex)
155             {
156                 throw ex;
157             }
158         }
159 
160         private static void SendCallback(IAsyncResult ar)
161         {
162             try
163             {
164                 Socket handler = (Socket)ar.AsyncState;
165                 handler.EndSend(ar);
166             }
167             catch (SocketException ex)
168             {
169                 throw ex;
170             }
171         }
172 
173         /// <summary>
174         /// 接收数据8个字节
175         /// </summary>
176         static byte[] MsgBuffer = new byte[8];
177 
178         /// <summary>
179         /// 接收消息
180         /// </summary>
181         public static void ReceiveData()
182         {
183             serverSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallback), null);
184         }
185 
186         /// <summary>
187         /// 回调接收数据
188         /// </summary>
189         /// <param name="ar"></param>
190         private static void ReceiveCallback(IAsyncResult ar)
191         {
192             try
193             {
194                 int REnd = serverSocket.EndReceive(ar);
195                 if (REnd > 0)
196                 {
197                     byte[] data = new byte[REnd];
198                     Array.Copy(MsgBuffer, 0, data, 0, REnd);
199                     int Msglen = data.Length;
200                     //在此次可以对data进行按需处理
201                     serverSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallback), null);
202                     Console.WriteLine("来自到客户端{0}字节:{1} \tTime:{2}",REnd ,UTF8Encoding .UTF8.GetString(data, 0, Msglen),DateTime.Now.ToString()+ DateTime.Now.Millisecond.ToString());
203                 }
204                 else
205                 {
206                     dispose();
207                 }
208             }
209             catch (SocketException ex)
210             {
211                 throw ex;
212             }
213         }
214 
215         /// <summary>
216         /// 释放连接
217         /// </summary>
218         private static void dispose()
219         {
220             try
221             {
222                 serverSocket.Shutdown(SocketShutdown.Both);
223                 serverSocket.Close();
224             }
225             catch (Exception ex)
226             {
227                 throw ex;
228             }
229         }
230         #endregion
231     }
232 }

客户端 TcpClient.cs
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 using System.Net;
  7 using System.Net.Sockets;
  8 using System.Threading;
  9 
 10 namespace TcpClient
 11 {
 12     class Program
 13     {
 14         static int port = 6000;                          //监听端口号
 15         static string host = "127.0.0.1";                //连接服务端IP
 16         static IPAddress ip = IPAddress.Parse(host);     //将IP地址转换为IP实例
 17         static IPEndPoint ipe = new IPEndPoint(ip, port);//将网络端点表示为 IP 地址和端口号
 18         static Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//建立客户端Socket
 19 
 20         static Thread thread_client;
 21         static void Main(string[] args)
 22         {
 23             Connect(ip, port);//异步连接
 24             string sendStr = "Nice to meet you,Server!";
 25             thread_client = new Thread(ClientConnectOrNot);
 26             Thread.Sleep(0);
 27             thread_client.Start();
 28             {
 29                 Console.WriteLine("服务器->客户端:" + clientSocket.RemoteEndPoint + "->" + clientSocket.LocalEndPoint);
 30                 Send(sendStr);  //发送消息
 31                 ReceiveData();  //接收消息
 32                 sendStr = Console.ReadLine();
 33             }
 34            
 35         }
 36         /// <summary>
 37         /// 判断服务端是否关闭
 38         /// </summary>
 39         public static void ClientConnectOrNot()
 40         {
 41             if (clientSocket.Poll(-1, SelectMode.SelectRead) == true) //检测服务端是否关闭
 42             {
 43                 Console.WriteLine(clientSocket.RemoteEndPoint+"服务端已关闭...");
 44 
 45                 //thread_client.Abort();
 46                 //Thread exit = new Thread(ApplicationExit); //自动关闭程序
 47                 //Thread.Sleep(3000);
 48                 //exit.Start();
 49                 
 50             }
 51             else
 52             {
 53                 Console.WriteLine(thread_client.ThreadState);
 54             }
 55         }
 56         /// <summary>
 57         /// 退出程序关闭Dos窗口
 58         /// </summary>
 59         public static void ApplicationExit()
 60         {
 61             Environment.Exit(0);
 62         }
 63 
 64         #region
 65         /// <summary>
 66         /// 异步连接
 67         /// </summary>
 68         /// <param name="ip"></param>
 69         /// <param name="port"></param>
 70         /// <param name="clientSocket"></param>
 71         public static void Connect(IPAddress ip, int port)
 72         {
 73             clientSocket.BeginConnect(ip, port, new AsyncCallback(ConnectCallback), clientSocket);
 74         }
 75 
 76         private static void ConnectCallback(IAsyncResult ar)
 77         {
 78             try
 79             {
 80                 Socket handler = (Socket)ar.AsyncState;
 81                 handler.EndConnect(ar);
 82             }
 83             catch (SocketException ex)
 84             {
 85                 throw ex;
 86             }
 87         }
 88         /// <summary>
 89         /// 发送数据
 90         /// </summary>
 91         /// <param name="data"></param>
 92         public static void Send(string data)
 93         {
 94             //Send(System.Text.Encoding.UTF8.GetBytes(data));
 95             //Send(Encoding.ASCII.GetBytes(data));
 96             Send(UTF8Encoding.UTF8.GetBytes(data));
 97         }
 98         /// <summary>
 99         /// 发送数据
100         /// </summary>
101         /// <param name="byteData"></param>
102         private static void Send(byte[] byteData)
103         {
104             try
105             {
106                 int length = byteData.Length;
107                 //byte[] head = BitConverter.GetBytes(length);
108                 byte[] head = Encoding.ASCII.GetBytes(length.ToString()); //头部表示字节的总长度
109                 byte[] data = new byte[head.Length + byteData.Length];
110                 Array.Copy(head, data, head.Length);
111                 Array.Copy(byteData, 0, data, head.Length, byteData.Length);
112                 clientSocket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), clientSocket);
113             }
114             catch (SocketException ex)
115             { }
116         }
117         /// <summary>
118         /// 回调发送
119         /// </summary>
120         /// <param name="ar"></param>
121         private static void SendCallback(IAsyncResult ar)
122         {
123             try
124             {
125                 Socket handler = (Socket)ar.AsyncState;
126                 handler.EndSend(ar);
127             }
128             catch (SocketException ex)
129             {
130                 throw ex;
131             }
132         }
133 
134         /// <summary>
135         /// 定义缓冲区接收数据8个字节
136         /// </summary>
137         static byte[] MsgBuffer = new byte[8];
138 
139         /// <summary>
140         /// 接收消息
141         /// </summary>
142         public static void ReceiveData()
143         {
144             clientSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallback), null);
145         }
146         static bool first = true;
147         private static void ReceiveCallback(IAsyncResult ar)
148         {
149             try
150             {
151                 int REnd = clientSocket.EndReceive(ar);
152                 if (REnd > 0)
153                 {
154                     byte[] data = new byte[REnd];
155                     Array.Copy(MsgBuffer, 0, data, 0, REnd);
156 
157                     int Msglen = data.Length;
158                     //在此次可以对data进行按需处理
159                     
160                     clientSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallback), null);
161                     string revStr = UTF8Encoding .UTF8.GetString(data, 0, Msglen);
162                     if (first)
163                     {
164                         string len = revStr.Substring(0, 2);
165                         first = false;
166                     }
167                     Console.WriteLine("来自服务端{0}字节:{1}\tTime:{2}",REnd,revStr,DateTime.Now.ToString()+ DateTime.Now.Millisecond.ToString());
168                 }
169                 else
170                 {
171                     dispose();
172                 }
173             }
174             catch (SocketException ex)
175             { }
176         }
177         /// <summary>
178         /// 释放Socket
179         /// </summary>
180         private static void dispose()
181         {
182             try
183             {
184                 clientSocket.Shutdown(SocketShutdown.Both);
185                 clientSocket.Close();
186             }
187             catch (Exception ex)
188             {
189                 throw ex;
190             }
191         }
192         #endregion
193     }
194 }
先打开服务端:
开始->运行,输入cmd确定再输入netstat -a 查看端口监听状态


接下来运行客户端:
下面咱们稍微改下代码,使客户端和服务端能进行数据交互。
服务器端:
 
开始进行会话了,
客户端:你好吗?
服务端:我很好,你呢?
客户端:I'm fine too!
咱么断开客户端,看看你服务端的反应:
若是断开服务端:
总结:实验发现异步通信可以利用一个Socket进行多次数据传输,自己定义的缓冲区用于接收数据,如果定义的缓冲区很大不会发生拆包现象,如果很小则会自动按缓冲区大小分割。