前言
Socket的英文原義是“孔”或“插座”,其實在網絡程式設計中Socket就是這個意思,就像我們打電話,要首先知道對方的手機号一樣,這個手機号就相當于一個Socket号、一個插座,在網絡程式設計中就是ip+端口作為一個插座。
實作
System.Net.Sockets命名空間下提供了Socket類,使.net下Socket變得很簡單,Socket實作點對點通信有兩種方式,一種是用伺服器轉接,所有的用戶端都發送到服務端,用戶端隻做用戶端;另一種是用戶端既是服務端又是服務端,就是既監聽又發送資訊。這篇就用第二種方式簡單實作下,首先看下簡單示意圖:
發送資訊代碼:
1 string message = txtMsg.Text.Trim();
2 socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
3 string remoteIp = this.txtRemoteIP.Text;
4 string remotePort = this.txtRemotePort.Text;
5 int serverPort = Convert.ToInt32(remotePort);
6 IPAddress serverIp = IPAddress.Parse(remoteIp);
7 IPEndPoint remoteIep = new IPEndPoint(serverIp, serverPort);
8 socketClient.Connect(remoteIep);
9 toolStripStatusLabel1.Text = "與遠端計算機" + remoteIp + ":" + remotePort + "建立連接配接!";
10
11 byte[] byteMessage = Encoding.Default.GetBytes(message);
12 socketClient.Send(byteMessage);
13
14 IPHostEntry host = Dns.GetHostEntry(GetServerIP());
15 string HostName = host.HostName;
16
17 //發送資訊
18 string time1 = DateTime.Now.ToString();
19 listBox1.Items.Add(GetServerIP().ToString() + "(" + HostName + ") " + time1);
20 listBox1.Items.Add(message);
21
22 socketClient.Shutdown(SocketShutdown.Both);
23 socketClient.Close();
IPEndPoint從這個單詞的意思就可以看出是一個遠端的位址資訊,Connect方法根據這個位址建立連結,然後調用Send方法向遠端發送資訊,發送完資訊之後要使用Shutdown指向目前Socket是否接受發送消息,下面列出SocketShutdown的枚舉值:
值 | 描述 |
---|---|
Send | 禁用此 Socket 上的發送。 |
Receive | 禁用此 Socket 上的接收。 |
Both | 同時禁用此 Socket 上的發送和接收。 |
Shutdown在msdn上解釋是這樣的:如果目前使用的是面向連接配接的Socket,則必須先調用 Shutdown 方法,然後才能關閉Socket。這可以確定在已連接配接的套接字關閉之前,已發送和接收該套接字上的所有資料。Shutdown也是關閉的意思,其實關于Shutdown和Close我在網上找了很多資料,關于Shutdown解釋的雲裡霧裡,不是很明白,我這樣了解不知道對不對,上面打電話的例子,按号碼打通電話說完話按下挂機鍵,Shutdown的意思這樣,確定資訊已經發送。
監聽代碼:
1 socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
2
3 IPAddress ServerIp = GetServerIP();
4 IPEndPoint iep = new IPEndPoint(ServerIp, port);
5 socketServer.Bind(iep);
6
7 while (true)
8 {
9 try
10 {
11 socketServer.Listen(5);
12 allDone.Reset();
13 socketServer.BeginAccept(new AsyncCallback(AcceptCallback), socketServer);
14 allDone.WaitOne();
15 }
16 catch (SocketException ex)
17 {
18 toolStripStatusLabel1.Text += ex.ToString();
19 }
20 }
Bind與本機綁定開通這個“号碼”以友善别人可以打進來,Listen(5)5的意思是最大的監聽數,BeginAccept的意思是開始一個異步操作來接受一個傳入的連接配接嘗試,以異步方式接受連接配接将使您能夠在單獨的執行線程中發送和接收資料,回調方法使用EndAccept,并傳回新的Socket對象。
1 //異步連接配接回調函數
2 public void AcceptCallback(IAsyncResult ar)
3 {
4 Socket listener = (Socket)ar.AsyncState;
5 Socket client = listener.EndAccept(ar);
6 allDone.Set();
7 StateObject state = new StateObject();
8 state.workSocket = client;
9 client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(readCallback), state);
10 }
11
12 //異步接收回調函數
13 public void readCallback(IAsyncResult ar)
14 {
15 StateObject state = (StateObject)ar.AsyncState;
16 Socket handler = state.workSocket;
17 int bytesRead = handler.EndReceive(ar);
18 if (bytesRead > 0)
19 {
20 string strmsg = Encoding.Default.GetString(state.buffer, 0, bytesRead);
21
22 //遠端資訊
23 EndPoint tempRemoteEP = handler.RemoteEndPoint;
24 IPEndPoint tempRemoteIP = (IPEndPoint)tempRemoteEP;
25 IPHostEntry host = Dns.GetHostByAddress(tempRemoteIP.Address);
26 string HostName = host.HostName;
27
28 string ip = tempRemoteIP.Address.ToString() + "(" + HostName + ") " + DateTime.Now.ToString();
29 if (listBox1.InvokeRequired)
30 {
31 MyDelegate md;
32 md = new MyDelegate(ChangeText);
33 listBox1.Invoke(md, ip, strmsg);
34 }
35 }
36 }
listener.EndAccept(ar)和handler.EndReceive(ar)取回遠端Socket對象,這邊注意下擷取IPHostEntry對象并不是用GetHostEntry方法,而是GetHostByAddress方法,使用GetHostEntry方法會産生異常,異步調用傳輸對象StateObject:
1 //異步傳遞的狀态對象
2 public class StateObject
3 {
4 public Socket workSocket = null;
5 public const int BufferSize = 1024;
6 public byte[] buffer = new byte[BufferSize];
7 }
運作截圖:

完整代碼:
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9
10 using System.Threading;
11 using System.Net;
12 using System.Net.Sockets;
13
14 namespace ClientSocket
15 {
16 public partial class Form1 : Form
17 {
18 Socket socketClient;
19 Thread mythread;//建立線程
20 Socket socketServer;
21 int port = 8080;//定義偵聽端口号
22 public static ManualResetEvent allDone = new ManualResetEvent(false);
23 public delegate void MyDelegate(string ip,string message);
24
25 public Form1()
26 {
27 InitializeComponent();
28 }
29 private void Form1_Load(object sender, EventArgs e)
30 {
31 mythread = new Thread(new ThreadStart(BeginListen));
32 mythread.Start();
33
34 txtRemoteIP.Text = GetServerIP().ToString();
35 }
36
37 #region server
38 //擷取本機IP位址
39 public static IPAddress GetServerIP()
40 {
41 IPHostEntry ieh = Dns.GetHostEntry(Dns.GetHostName());
42 foreach (IPAddress item in ieh.AddressList)
43 {
44 if (item.ToString().IndexOf("192.168.0.")>=0)
45 {
46 return item;
47 }
48 }
49 return null;
50 }
51
52 //異步傳遞的狀态對象
53 public class StateObject
54 {
55 public Socket workSocket = null;
56 public const int BufferSize = 1024;
57 public byte[] buffer = new byte[BufferSize];
58 }
59
60 //監聽
61 private void BeginListen()
62 {
63 socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
64
65 IPAddress ServerIp = GetServerIP();
66 IPEndPoint iep = new IPEndPoint(ServerIp, port);
67 socketServer.Bind(iep);
68
69 while (true)
70 {
71 try
72 {
73 socketServer.Listen(5);
74 allDone.Reset();
75 socketServer.BeginAccept(new AsyncCallback(AcceptCallback), socketServer);
76 allDone.WaitOne();
77 }
78 catch (SocketException ex)
79 {
80 toolStripStatusLabel1.Text += ex.ToString();
81 }
82 }
83 }
84
85 //異步連接配接回調函數
86 public void AcceptCallback(IAsyncResult ar)
87 {
88 Socket listener = (Socket)ar.AsyncState;
89 Socket client = listener.EndAccept(ar);
90 allDone.Set();
91 StateObject state = new StateObject();
92 state.workSocket = client;
93 client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(readCallback), state);
94 }
95
96 //異步接收回調函數
97 public void readCallback(IAsyncResult ar)
98 {
99 StateObject state = (StateObject)ar.AsyncState;
100 Socket handler = state.workSocket;
101 int bytesRead = handler.EndReceive(ar);
102 if (bytesRead > 0)
103 {
104 string strmsg = Encoding.Default.GetString(state.buffer, 0, bytesRead);
105
106 //遠端資訊
107 EndPoint tempRemoteEP = handler.RemoteEndPoint;
108 IPEndPoint tempRemoteIP = (IPEndPoint)tempRemoteEP;
109 IPHostEntry host = Dns.GetHostByAddress(tempRemoteIP.Address);
110 string HostName = host.HostName;
111
112 string ip = tempRemoteIP.Address.ToString() + "(" + HostName + ") " + DateTime.Now.ToString();
113 if (listBox1.InvokeRequired)
114 {
115 MyDelegate md;
116 md = new MyDelegate(ChangeText);
117 listBox1.Invoke(md, ip, strmsg);
118 }
119 }
120 }
121
122 public void ChangeText(string ip,string message)
123 {
124 listBox1.Items.Add(ip);
125 listBox1.Items.Add(message);
126 }
127
128 #endregion
129
130 #region client
131 //發送資訊
132 private void btn_Send_Click(object sender, EventArgs e)
133 {
134 try
135 {
136 string message = txtMsg.Text.Trim();
137 socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
138 string remoteIp = this.txtRemoteIP.Text;
139 string remotePort = this.txtRemotePort.Text;
140 int serverPort = Convert.ToInt32(remotePort);
141 IPAddress serverIp = IPAddress.Parse(remoteIp);
142 IPEndPoint remoteIep = new IPEndPoint(serverIp, serverPort);
143 socketClient.Connect(remoteIep);
144 toolStripStatusLabel1.Text = "與遠端計算機" + remoteIp + ":" + remotePort + "建立連接配接!";
145
146 byte[] byteMessage = Encoding.Default.GetBytes(message);
147 socketClient.Send(byteMessage);
148
149 IPHostEntry host = Dns.GetHostEntry(GetServerIP());
150 string HostName = host.HostName;
151
152 //發送資訊
153 string time1 = DateTime.Now.ToString();
154 listBox1.Items.Add(GetServerIP().ToString() + "(" + HostName + ") " + time1);
155 listBox1.Items.Add(message);
156
157 socketClient.Shutdown(SocketShutdown.Both);
158 socketClient.Close();
159 }
160 catch
161 {
162 toolStripStatusLabel1.Text = "無法連接配接到目标計算機!";
163 return;
164 }
165 }
166
167 private void btnClose_Click(object sender, EventArgs e)
168 {
169 Application.Exit();
170 }
171 #endregion
172 }
173 }
View Code
程式下載下傳:Socket點對點通信.rar
附錄:小菜學習程式設計-Winform系列(初學者)
作者:田園裡的蟋蟀
微信公衆号:你好架構
出處:http://www.cnblogs.com/xishuai/
公衆号會不定時的分享有關架構的方方面面,包含并不局限于:Microservices(微服務)、Service Mesh(服務網格)、DDD/TDD、Spring Cloud、Dubbo、Service Fabric、Linkerd、Envoy、Istio、Conduit、Kubernetes、Docker、MacOS/Linux、Java、.NET Core/ASP.NET Core、Redis、RabbitMQ、MongoDB、GitLab、CI/CD(持續內建/持續部署)、DevOps等等。
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接。
分享到:
QQ空間
新浪微網誌
騰訊微網誌
微信
更多