天天看點

小菜學習Winform(三)Socket點對點通信

前言

  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     }      

  運作截圖:

小菜學習Winform(三)Socket點對點通信

  完整代碼:

小菜學習Winform(三)Socket點對點通信
小菜學習Winform(三)Socket點對點通信
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空間

新浪微網誌

騰訊微網誌

微信

更多

繼續閱讀