同步socket, 异步socket, 多线程socket, 多进程socket

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了同步socket, 异步socket, 多线程socket, 多进程socket相关的知识,希望对你有一定的参考价值。

参考技术A socket常用,本文立足同步和异步socket,以及现有的socketserver库。

同步socket一般有利用socket库直接,就可以写出tcp或udp的套接字

socketserver提供的线程或进程方式的socket

利用python 3.5+的asyncio协议,封装一个协程的socket server ,普通的socket客户也可以连接。

服务器端

客户端

为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据

构造报头信息

服务端

客户端

粘包,分包都tcp
tcp为什么会有粘包分包这些情况:
1.服务端处理不过来
2.客户端采用优化纳格尔算法,达到一定字节才发
怎么处理:
1. 客,服双方确定包头规范,根据包头的信息取包长度
2. 客户端发送带上标记位,如\n, 服务端根据标记取包

服务器端

客户端

服务器端

客户端

服务器端

客户端

封装了socket,而且解决了Io阻塞问题

服务端

客户端

客户端

参考:
http://www.cnblogs.com/jokerbj/p/7422349.html

http://xiaorui.cc/2016/03/08/%E8%A7%A3%E5%86%B3golang%E5%BC%80%E5%8F%91socket%E6%9C%8D%E5%8A%A1%E6%97%B6%E7%B2%98%E5%8C%85%E5%8D%8A%E5%8C%85bug/

http://xiaorui.cc/2016/04/15/%E6%89%93%E9%80%A0mvc%E6%A1%86%E6%9E%B6%E4%B9%8Bsocket%E8%8E%B7%E5%8F%96http%E7%B2%98%E5%8C%85%E9%97%AE%E9%A2%98/

https://www.jianshu.com/p/065c53cab328

https://mozillazg.com/2017/08/python-asyncio-note-io-protocol.html#hidid3

网络编程-Socket编程(异步通讯)(Tcp,Udp)

Socket编程(异步通讯)(Tcp,Udp)

上一章主要展示了Socket的TcpUdp两种协议下的基本通讯方式,属于同步通讯。至于一个服务器对应多个客户端,或者对应多个请求,我们采用的是多线程的方式来解决此问题。然而本章节我们将有更好的方式去实现它:Socket在TcpUdp两种协议下的异步通讯方式。

基于Tcp协议异步:

  BeginAccept方法和EndAccept方法

  包含在System.Net.Sockets命名空间下。异步Tcp使用BeginAccept方法开始接受新的客户端连接请求,该方法中系统自动利用线程池创建需要的线程,并在操作完成时利用异步回调机制调用提供给它的方法,同时返回相应的状态参数,然后方可利用EndAccept方法结束该连接请求.

  BeginRecive方法和EndRecive方法

  异步Tcp使用BeginRecive方法和开始接受客户端发送的的消息,该方法如上同理,接受完毕后调用回调函数传递相应的状态参数。利用EndRecive方法接受接受消息。

  至于BeginSend方法和EndSend方法、BeginConnect方法和EndConnect方法与上类似。

下面我们来看看如何在Tcp协议下进行客户端与服务器端之间的通讯:

服务器端: 

技术图片
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Text;
  4 #region 命名空间
  5 using System.Net;
  6 using System.Net.Sockets;
  7 using System.Threading;
  8 #endregion
  9 
 10 namespace AsynServerConsole
 11 {
 12     /// <summary>
 13     /// Tcp协议异步通讯类(服务器端)
 14     /// </summary>
 15     public class AsynTcpServer
 16     {
 17         #region Tcp协议异步监听
 18         /// <summary>
 19         /// Tcp协议异步监听
 20         /// </summary>
 21         public void StartListening()
 22         {
 23             //主机IP
 24             IPEndPoint serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8686);
 25             Socket tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 26             tcpServer.Bind(serverIp);
 27             tcpServer.Listen(100);
 28             Console.WriteLine("异步开启监听...");
 29             AsynAccept(tcpServer);
 30         }
 31         #endregion
 32 
 33         #region 异步连接客户端
 34         /// <summary>
 35         /// 异步连接客户端
 36         /// </summary>
 37         /// <param name="tcpServer"></param>
 38         public void AsynAccept(Socket tcpServer)
 39         {
 40             tcpServer.BeginAccept(asyncResult =>
 41             {
 42                 Socket tcpClient = tcpServer.EndAccept(asyncResult);
 43                 Console.WriteLine("server<--<--{0}", tcpClient.RemoteEndPoint.ToString());
 44                 AsynSend(tcpClient, "收到连接...");//发送消息
 45                 AsynAccept(tcpServer);
 46                 AsynRecive(tcpClient);
 47             }, null);
 48         }
 49         #endregion
 50 
 51         #region 异步接受客户端消息
 52         /// <summary>
 53         /// 异步接受客户端消息
 54         /// </summary>
 55         /// <param name="tcpClient"></param>
 56         public void AsynRecive(Socket tcpClient)
 57         {
 58             byte[] data = new byte[1024];
 59             try
 60             {
 61                 tcpClient.BeginReceive(data, 0, data.Length, SocketFlags.None,
 62                 asyncResult =>
 63                 {
 64                     int length = tcpClient.EndReceive(asyncResult);
 65                     Console.WriteLine("server<--<--client:{0}", Encoding.UTF8.GetString(data));
 66                     AsynSend(tcpClient, "收到消息...");
 67                     AsynRecive(tcpClient);
 68                 }, null);
 69             }
 70             catch (Exception ex)
 71             {
 72                 Console.WriteLine("异常信息:", ex.Message);
 73             }
 74         }
 75         #endregion
 76 
 77         #region 异步发送消息
 78         /// <summary>
 79         /// 异步发送消息
 80         /// </summary>
 81         /// <param name="tcpClient">客户端套接字</param>
 82         /// <param name="message">发送消息</param>
 83         public void AsynSend(Socket tcpClient, string message)
 84         {
 85             byte[] data = Encoding.UTF8.GetBytes(message);
 86             try
 87             {
 88                 tcpClient.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
 89                 {
 90                     //完成发送消息
 91                     int length = tcpClient.EndSend(asyncResult);
 92                     Console.WriteLine("server-->-->client:{0}", message);
 93                 }, null);
 94             }
 95             catch (Exception ex)
 96             {
 97                 Console.WriteLine("异常信息:{0}", ex.Message);
 98             }
 99         }
100         #endregion
101     }
102 }
View Code

客户端:

技术图片
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Text;
 4 #region 命名空间
 5 using System.Net;
 6 using System.Net.Sockets;
 7 using System.Threading;
 8 #endregion
 9 
10 namespace AsynClientConsole
11 {
12     /// <summary>
13     /// Tcp协议异步通讯类(客户端)
14     /// </summary>
15     public class AsynTcpClient
16     {
17         #region 异步连接
18         /// <summary>
19         /// Tcp协议异步连接服务器
20         /// </summary>
21         public void AsynConnect()
22         {
23             //主机IP
24             IPEndPoint serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8686);
25             Socket tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
26             tcpClient.BeginConnect(serverIp, asyncResult =>
27             {
28                 tcpClient.EndConnect(asyncResult);
29                 Console.WriteLine("client-->-->{0}", serverIp.ToString());
30                 AsynSend(tcpClient, "我上线了...");
31                 AsynSend(tcpClient, "第一次发送消息...");
32                 AsynSend(tcpClient, "第二次发送消息...");
33                 AsynRecive(tcpClient);
34             }, null);
35         }
36         #endregion
37 
38         #region 异步接受消息
39         /// <summary>
40         /// 异步连接客户端回调函数
41         /// </summary>
42         /// <param name="tcpClient"></param>
43         public void AsynRecive(Socket tcpClient)
44         {
45             byte[] data = new byte[1024];
46             tcpClient.BeginReceive(data, 0, data.Length, SocketFlags.None, asyncResult =>
47             {
48                 int length = tcpClient.EndReceive(asyncResult);
49                 Console.WriteLine("client<--<--server:{0}", Encoding.UTF8.GetString(data));
50                 AsynRecive(tcpClient);
51             }, null);
52         }
53         #endregion
54 
55         #region 异步发送消息
56         /// <summary>
57         /// 异步发送消息
58         /// </summary>
59         /// <param name="tcpClient">客户端套接字</param>
60         /// <param name="message">发送消息</param>
61         public void AsynSend(Socket tcpClient, string message)
62         {
63             byte[] data = Encoding.UTF8.GetBytes(message);
64             tcpClient.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
65             {
66                 //完成发送消息
67                 int length = tcpClient.EndSend(asyncResult);
68                 Console.WriteLine("client-->-->server:{0}", message);
69             }, null);
70         }
71         #endregion
72     }
73 }
View Code

通讯效果如下图:

服务器:

技术图片

客户端:

技术图片

上面我们完成了基于Tcp协议下的Socket通讯,那么Udp协议下的通讯我们将以什么样的形式来通讯呢?毕竟Udp协议下是无连接模式。

基于Udp协议的异步通讯:

  其实与Tcp协议具有的方法类似,但由于Udp协议是无连接模式,我们所用到方法就无BeginConnect和EndConnect方法。我们所要做的就是收发消息的处理。

  在Udp协议的异步通讯中,我们需要注意一下几个编程点:

  1.在EndRecive方法中,由于无状态返回模式,不能返回发送端的Remote,所以我们需要在该方法中获取活动端的Remote,然后利用EndRecive方法结束接受该消息接受。

  2.客户端由于无需Connect到服务器端,但是需要先向服务器端发送一个请求如Send一些消息。让服务器端确定自己Remote,然后可利用Recive方法接收其他终端发送过来的消息。

下面将演示Udp协议下异步通讯:

服务器端:

技术图片
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Text;
 4 #region 命名空间
 5 using System.Net;
 6 using System.Net.Sockets;
 7 using System.Threading;
 8 #endregion
 9 
10 namespace AsynServerConsole
11 {
12     /// <summary>
13     /// Udp协议异步通讯类(服务器端)
14     /// </summary>
15     public class AsynUdpServer
16     {
17         #region 容器对象
18         /// <summary>
19         /// 容器对象
20         /// </summary>
21         public class StateObject
22         {
23             //服务器端
24             public Socket udpServer = null;
25             //接受数据缓冲区
26             public byte[] buffer = new byte[1024];
27             //远程终端
28             public EndPoint remoteEP;
29         }
30 
31         public StateObject state;
32         #endregion
33 
34         #region 服务器绑定终端节点
35         public void ServerBind()
36         {
37             //主机IP
38             IPEndPoint serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8686);
39             Socket udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
40             udpServer.Bind(serverIp);
41             Console.WriteLine("server ready...");
42             IPEndPoint clientIp = new IPEndPoint(IPAddress.Any, 0);
43             state = new StateObject();
44             state.udpServer = udpServer;
45             state.remoteEP = (EndPoint)clientIp;
46             AsynRecive();
47         }
48         #endregion
49 
50         #region 异步接受消息
51         public void AsynRecive()
52         {
53             state.udpServer.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, SocketFlags.None, ref state.remoteEP,
54                 new AsyncCallback(ReciveCallback), null);
55         }
56         #endregion
57 
58         #region 异步接受消息回调函数
59         public void ReciveCallback(IAsyncResult asyncResult)
60         {
61             if (asyncResult.IsCompleted)
62             {
63                 //获取发送端的终节点
64                 IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 0);
65                 EndPoint remoteEP = (EndPoint)ipep;
66                 state.udpServer.EndReceiveFrom(asyncResult, ref remoteEP);
67                 Console.WriteLine("server<--<--client:{0}", Encoding.UTF8.GetString(state.buffer));
68                 //向发送端通知:收到消息
69                 state.remoteEP = remoteEP;
70                 AsynSend("收到消息");
71                 //继续接受消息
72                 AsynRecive();
73             }
74         }
75         #endregion
76 
77         #region 异步发送消息
78         public void AsynSend(string message)
79         {
80             Console.WriteLine("server-->-->client:{0}", message);
81             byte[] buffer = Encoding.UTF8.GetBytes(message);
82             state.udpServer.BeginSendTo(buffer, 0, buffer.Length, SocketFlags.None, state.remoteEP,
83                 new AsyncCallback(SendCallback), null);
84         }
85         #endregion
86 
87         #region 异步发送消息回调函数
88         public void SendCallback(IAsyncResult asyncResult)
89         {
90             //消息发送完毕
91             if (asyncResult.IsCompleted)
92             {
93                 state.udpServer.EndSendTo(asyncResult);
94             }
95         }
96         #endregion
97     }
98 }
View Code

客户端:

技术图片
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Text;
 4 #region 命名空间
 5 using System.Net;
 6 using System.Net.Sockets;
 7 using System.Threading;
 8 #endregion
 9 
10 namespace AsynClientConsole
11 {
12     /// <summary>
13     /// Udp协议异步通讯类(客户端)
14     /// </summary>
15     public class AsynUdpClient
16     {
17         #region 容器对象
18         /// <summary>
19         /// 容器对象
20         /// </summary>
21         public class StateObject
22         {
23             //客户端套接字
24             public Socket udpClient = null;
25             //接收信息缓冲区
26             public byte[] buffer = new byte[1024];
27             //服务器端终节点
28             public IPEndPoint serverIp;
29             //远程终端节点
30             public EndPoint remoteEP;
31         }
32 
33         public StateObject state;
34         #endregion
35 
36         #region 客户端初始化
37         public void InitClient()
38         {
39             state = new StateObject();
40             state.udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
41             state.serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8686);
42             state.remoteEP = (EndPoint)(new IPEndPoint(IPAddress.Any, 0));
43             //此处注意:
44             //  由于当前是客户端,所以没有绑定终节点
45             //  不可直接接收消息,必须先向其他终端发送信息告知本机终节点
46             AsynSend("第1次发送消息");
47             AsynSend("第2次发送消息");
48             AsynRecive();
49         }
50         #endregion
51 
52         #region 异步接收来自其他终端发送的消息
53         public void AsynRecive()
54         {
55             state.udpClient.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, SocketFlags.None, ref state.remoteEP,
56                 new AsyncCallback(ReciveCallback), null);
57         }
58         #endregion
59 
60         #region 异步接收来自其他终端发送的消息回调函数
61         public void ReciveCallback(IAsyncResult asyncResult)
62         {
63             //信息接收完成
64             if (asyncResult.IsCompleted)
65             {
66                 state.udpClient.EndReceiveFrom(asyncResult, ref state.remoteEP);
67                 Console.WriteLine("client<--<--{0}:{1}", state.remoteEP.ToString(), Encoding.UTF8.GetString(state.buffer));
68                 AsynRecive();
69             }
70         }
71         #endregion
72 
73         #region 异步发送消息
74         public void AsynSend(string message)
75         {
76             Console.WriteLine("client-->-->{0}:{1}", state.serverIp.ToString(), message);
77             byte[] buffer = Encoding.UTF8.GetBytes(message);
78             state.udpClient.BeginSendTo(buffer, 0, buffer.Length, SocketFlags.None, state.serverIp,
79                 new AsyncCallback(SendCallback), null);
80         }
81         #endregion
82 
83         #region 异步发送消息回调函数
84         public void SendCallback(IAsyncResult asyncResult)
85         {
86             //消息发送完成
87             if (asyncResult.IsCompleted)
88             {
89                 state.udpClient.EndSendTo(asyncResult);
90             }
91         }
92         #endregion
93     }
94 }
View Code

通讯效果如下图:

 

服务器:

技术图片

 

客户端:

技术图片

 

总结:基于异步模式的通讯无须采用多线程来服务多个客户端以及多个请求,这样的通讯模式效率更高。

  同步上面Tcp效果展示图,我们发现客户端分几次连续发送的消息被服务器端一次接收了,读成了一条数据,而这就是Socket通讯基于Tcp协议下发生的粘包问题,下面一种我们将着重对Tcp协议的通讯信息封包,拆包以解决上面问题。

  同样Udp协议通讯下属于无连接模式通讯,客户端只管将消息发送出去,或者由于网络原因,而造成的丢包问题,下一章也将采用一定的方式解决。

作者:曾庆雷
出处:http://www.cnblogs.com/zengqinglei
本页版权归作者和博客园所有,欢迎转载,但未经作者同意必须保留此段声明, 且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利

以上是关于同步socket, 异步socket, 多线程socket, 多进程socket的主要内容,如果未能解决你的问题,请参考以下文章

GJM :C#开发 异步处理是目的,多线程是手段

网络编程-Socket编程(异步通讯)(Tcp,Udp)

网络编程-Socket编程(异步通讯)(Tcp,Udp)

C# socket服务器端 多线程客户端 如何少量使用CPU

c# socket多连接(如何达到10万个客户端)

WSASocket与socket