C# Socket學習筆記二
小記:昨天咱們已經了解了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 }