做.NET应用开发肯定会用到网络通信,而进程间通信是客户端开发使用频率较高的场景。

进程间通信方式主要有命名管道、消息队列、共享内存、Socket通信,个人使用最多的是Sokcet相关。

而Socket也有很多使用方式,Socket、WebSocket、TcpClient、UdpClient,是不是很多?HttpClient与TcpClient、WebSocket之间有什么关系?这里我们分别介绍下这些通信及使用方式

Socket

Socket是传输通信协议么?No,Socket是一种传输层和应用层之间、用于实现网络通信的编程接口。Socket可以基于各种协议如TCP、UDP协议实现进程通信,TCP/UDP才是传输通信协议

Socket位于传输层与应用层之间,接口在System.Net.Sockets命名空间下。下面是Socket以TCP通信的DEMO:

    //创建一个 Socket 实例
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //连接到服务器
clientSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8000)); //发送数据
string message = "Hello, Server!";
byte[] data = Encoding.ASCII.GetBytes(message);
clientSocket.Send(data); //接收数据
byte[] buffer = new byte[1024];
int bytesRead = clientSocket.Receive(buffer);
Debug.WriteLine(Encoding.ASCII.GetString(buffer, 0, bytesRead)); clientSocket.Close();

TcpClient/UdpClient

TCP/UDP均是位于传输层的通信协议,所以Socket的使用也是位于传输层的通信操作

TCP是面向连接,提供可靠、顺序的数据流传输。用于一对一的通信,即一个TCP连接只能有一个发送方和一个接收方。所以适用于需要数据完整性和可靠传输的场景

而UDP则是无连接的,不需要建立和维护连接状态,不提供确认机制,也不重传丢失的数据报,但也因此传输实时性高,适合低延时、数据量小、广播场景

基于Socket抽象编程接口,TCP、UDP构建更高级别抽象网络编程TcpClient、UdpClient,它们用于简化TCP网络编程中的常见任务

TcpClient 和 UdpClient 是 .NET 提供的用于方便管理 TCP 和 UDP 网络通信的类,下面是对应的Demo

Tcp服务端:

 1 using System;
2 using System.Net;
3 using System.Net.Sockets;
4 using System.Text;
5
6 class TcpServerExample
7 {
8 public static void Main()
9 {
10 TcpListener listener = new TcpListener(“127.0.0.1", 8000);
11 listener.Start();
12 Console.WriteLine("Server is listening on port 8000...");
13
14 TcpClient client = listener.AcceptTcpClient();
15 NetworkStream stream = client.GetStream();
16
17 byte[] data = new byte[1024];
18 int bytesRead = stream.Read(data, 0, data.Length);
19 Console.WriteLine("Received: " + Encoding.ASCII.GetString(data, 0, bytesRead));
20
21 byte[] response = Encoding.ASCII.GetBytes("Hello, Client!");
22 stream.Write(response, 0, response.Length);
23
24 stream.Close();
25 client.Close();
26 listener.Stop();
27 }
28 }

TCP客户端:

 1 using System;
2 using System.Net.Sockets;
3 using System.Text;
4
5 class TcpClientExample
6 {
7 public static void Main()
8 {
9 TcpClient client = new TcpClient("127.0.0.1", 8000);
10 NetworkStream stream = client.GetStream();
11
12 byte[] message = Encoding.ASCII.GetBytes("Hello, Server!");
13 stream.Write(message, 0, message.Length);
14
15 byte[] data = new byte[1024];
16 int bytesRead = stream.Read(data, 0, data.Length);
17 Debug.WriteLine("Received: " + Encoding.ASCII.GetString(data, 0, bytesRead));
18
19 stream.Close();
20 client.Close();
21 }
22 }

Udp服务端:

 1 using System;
2 using System.Net;
3 using System.Net.Sockets;
4 using System.Text;
5
6 class UdpServerExample
7 {
8 public static void Main()
9 {
10 UdpClient udpServer = new UdpClient(8000);
11 IPEndPoint remoteEP = new IPEndPoint(”127.0.0.1“, 0);
12
13 Console.WriteLine("Server is listening on port 8000...");
14
15 byte[] data = udpServer.Receive(ref remoteEP);
16 Console.WriteLine("Received: " + Encoding.ASCII.GetString(data));
17
18 byte[] response = Encoding.ASCII.GetBytes("Hello, Client!");
19 udpServer.Send(response, response.Length, remoteEP);
20
21 udpServer.Close();
22 }
23 }

Udp客户端:

 1 using System;
2 using System.Net;
3 using System.Net.Sockets;
4 using System.Text;
5
6 class UdpClientExample
7 {
8 public static void Main()
9 {
10 UdpClient udpClient = new UdpClient();
11 IPEndPoint remoteEP = new IPEndPoint(”127.0.0.1", 8000);
12
13 byte[] message = Encoding.ASCII.GetBytes("Hello, Server!");
14 udpClient.Send(message, message.Length, remoteEP);
15
16 byte[] data = udpClient.Receive(ref remoteEP);
17 Console.WriteLine("Received: " + Encoding.ASCII.GetString(data));
18
19 udpClient.Close();
20 }
21 }

上面是基本的网络通信DEMO,TcpClient用于基于连接、可靠的TCP通信,适用于需要数据完整性和可靠传输的场景。Udp用于无连接、不保证传输的UDP通信,适用于对实时性要求高、允许少量数据丢失的场景(如视频流)。会议场景下的传屏软件适合用这个协议,传屏发送端固定帧率一直推送,网络丢失几帧问题不大,重要的是延时低了很多。

TcpClient、UdpClient是位于传输层的通信类,分别实现了基于TCP和UDP协议的通信功能。

HttpClient

讲完传输层的网络通信类,就要说下应用层的HttpClient,这是专门用于HTTP协议的通信

Http与TCP/UDP均是网络通信协议,TCP、UDP位于传输层,HTTP传于应用层,而且HTTP是基于TCP面向连接的。HttpClient则是Http协议的通信类,提供了封装好的、高级的HTTP功能(如发起GET, POST请求,处理响应等)。

HttpClient可以用于Web接口的调用,我这边Windows应用的WebApi基础组件库就是用HttpClient实现的。

HttpClient类,在System.Net.Http.HttpClient命名空间下,HttpClient的内部实现是基于Socket的。也就是说,HttpClient底层使用Socket接口来建立连接并传输数据,但它隐藏了这些细节,为开发者提供了一个更简洁的API。

下面是我基于HttpClient实现的Web服务各类操作入口代码,可以简单浏览下:

 1         /// <summary>
2 /// 请求/推送数据
3 /// </summary>
4 /// <typeparam name="TResponse"></typeparam>
5 /// <param name="request"></param>
6 /// <returns></returns>
7 public async Task<TResponse> RequestAsync<TResponse>(HttpRequest request) where TResponse : HttpResponse, new()
8 {
9 var requestUrl = request.GetRequestUrl();
10 try
11 {
12 using var client = CreateHttpClient(request);
13 var requestMethod = request.GetRequestMethod();
14 switch (requestMethod)
15 {
16 case RequestMethod.Get:
17 {
18 using var response = await client.GetAsync(requestUrl);
19 return await response.GetTResponseAsync<TResponse>();
20 }
21 case RequestMethod.Post:
22 {
23 using var httpContent = request.GetHttpContent();
24 using var response = await client.PostAsync(requestUrl, httpContent);
25 return await response.GetTResponseAsync<TResponse>();
26 }
27 case RequestMethod.Put:
28 {
29 using var httpContent = request.GetHttpContent();
30 using var response = await client.PutAsync(requestUrl, httpContent);
31 return await response.GetTResponseAsync<TResponse>();
32 }
33 case RequestMethod.Delete:
34 {
35 using var response = await client.DeleteAsync(requestUrl);
36 return await response.GetTResponseAsync<TResponse>();
37 }
38 case RequestMethod.PostForm:
39 {
40 using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrl);
41 using var httpContent = request.GetHttpContent();
42 requestMessage.Content = httpContent;
43 using var response = await client.SendAsync(requestMessage);
44 return await response.GetTResponseAsync<TResponse>();
45 }
46 }
47 return new TResponse() { Message = $"不支持的请求类型:{requestMethod}" };
48 }
49 catch (ArgumentNullException e)
50 {
51 return new TResponse() { Code = NetErrorCodes.ParameterError, Message = e.Message, JsonData = e.StackTrace };
52 }
53 catch (TimeoutException e)
54 {
55 return new TResponse() { Code = NetErrorCodes.TimeOut, Message = e.Message, JsonData = e.StackTrace };
56 }
57 catch (Exception e)
58 {
59 return new TResponse() { Message = e.Message, JsonData = e.StackTrace };
60 }
61 }

HttpClient封装后的网络基础组件调用方式,也比较简单。

添加接口请求说明,参数及请求参数均统一在一个类文件里定义好:

 1 /// <summary>
2 /// 内网穿透注册接口
3 /// </summary>
4 [Request("http://frp.supporter.ws.h3c.com/user/register",RequestMethod.Post)]
5 [DataContract]
6 internal class RegisterFrpRequest : HttpRequest
7 {
8 public RegisterFrpRequest(string sn, string appName)
9 {
10 Sn = sn;
11 SeverNames = new List<RequestServiceName>()
12 {
13 new RequestServiceName(appName,"http")
14 };
15 }
16 [DataMember(Name = "sn")]
17 public string Sn { get; set; }
18
19 [DataMember(Name = "localServerNames")]
20 public List<RequestServiceName> SeverNames { get; set; }
21 }

再定义请求结果返回数据,基类HttpResponse内有定义基本参数,状态Success、状态码Code、返回描述信息Message:

 1 [DataContract]
2 class RegisterFrpResponse : HttpResponse
3 {
4
5 [DataMember(Name = "correlationId")]
6 public string CorrelationId { get; set; }
7
8 [DataMember(Name = "data")]
9 public FrpRegisterData Data { get; set; }
10
11 /// <summary>
12 /// 是否成功
13 /// </summary>
14 public bool IsSuccess => Success && Code == 200000 && Data != null;
15 }

然后,业务层可以进行简洁、高效率的调用:

var netClient = new NetHttpClient();

var response = await netClient.RequestAsync<RegisterFrpResponse>(new RegisterFrpRequest(sn, appName));

WebSocket

WebSocket也是一个应用层通信,不同于可以实现俩类协议TCP/UDP的Socket,WebSocket只依赖于HTTP/HTTPS连接。

一旦握手成功,客户端和服务器之间可以进行双向数据传输,可以传输字节数据也可以传输文本内容。

  • 持久连接:WebSocket 是持久化连接,除非主动关闭,否则在整个会话期间连接保持开放。

  • 全双工通信:客户端和服务器可以随时发送数据,通信不再是单向的。使用System.Net.WebSockets.ClientWebSocket类来实现WebSocket通信,通过减少 HTTP 请求/响应的开销、延时较低。

WebSocket与 HttpClient之间呢,都用于应用层的网络通信,但它们的用途和通信协议是不同的。

  • HttpClient使用 HTTP 协议,WebSocket 使用 WebSocket 协议,该协议在初始连接时通过 HTTP/HTTPS握手,然后转换为基于TCP通信的WebSocket协议。所以虽然都有使用HTTP协议,但WebSocket后续就切换至基于TCP的全双工通信了

  • HttpClient基于请求/响应模式,每次通信由客户端向服务器发起请求。WebSocket提供全双工通信,客户端和服务器都可以主动发送数据。

  • HttpClient主要用于访问 RESTful API、下载文件或者发送HTTP请求。WebSocket主要用于实现低延迟的实时通信,如进程间通信、局域网通信等。

我团队Windows应用所使用的进程间通信,就是基于WebSocketSharp封装的。WebSocketSharp是一个功能全面、易于使用的第三方 WebSocket 库 GitHub - sta/websocket-sharp

至于为啥不直接使用ClientWebSocket。。。是因为当时团队还未切换.NET,使用的是.NETFramework。

后面团队使用的局域网通信基础组件就是用ClientWebSocket了。

下面是我封装的部分WebSocket通信代码,事件发送(广播)、以及监听其它客户端发送过来的事件消息:

 1         /// <summary>
2 /// 发送消息
3 /// </summary>
4 /// <typeparam name="TInput">发送参数类型</typeparam>
5 /// <param name="client">目标客户端</param>
6 /// <param name="innerEvent">事件名</param>
7 /// <param name="data">发送参数</param>
8 /// <returns></returns>
9 public async Task<ClientResponse> SendAsync<TInput>(string client, InnerEventItem innerEvent, TInput data)
10 {
11 var message = new ChannelSendingMessage(client, new ClientEvent(innerEvent.EventName, innerEvent.EventId, true), _sourceClient);
12 message.SetData<TInput>(data);
13 return await SendMessageAsync(ChannelMessageType.ClientCommunication, message);
14 }
15
16 /// <summary>
17 /// 订阅消息
18 /// </summary>
19 /// <param name="client">目标客户端</param>
20 /// <param name="innerEvent">事件名称</param>
21 /// <param name="func">委托</param>
22 public ClientSubscribedEvent SubscribeFunc(string client, InnerEventItem innerEvent, Func<ClientResponse, object> func)
23 {
24 var eventName = innerEvent?.EventName;
25 if (string.IsNullOrEmpty(eventName) || func == null)
26 {
27 throw new ArgumentNullException($"{nameof(eventName)}或{nameof(func)},参数不能为空!");
28 }
29
30 var subscribedEvent = new ClientSubscribedEvent(client, innerEvent, func);
31 SubscribeEvent(subscribedEvent);
32 return subscribedEvent;
33 }
34
35 /// <summary>
36 /// 订阅消息
37 /// </summary>
38 /// <param name="client">目标客户端</param>
39 /// <param name="innerEvent">事件名称</param>
40 /// <param name="func">委托</param>
41 public ClientSubscribedEvent SubscribeFuncTask(string client, InnerEventItem innerEvent, Func<ClientResponse, Task<object>> func)
42 {
43 var eventName = innerEvent?.EventName;
44 if (string.IsNullOrEmpty(eventName) || func == null)
45 {
46 throw new ArgumentNullException($"{nameof(eventName)}或{nameof(func)},参数不能为空!");
47 }
48
49 var subscribedEvent = new ClientSubscribedEvent(client, innerEvent, func);
50 SubscribeEvent(subscribedEvent);
51 return subscribedEvent;
52 }

关键词:TCP/UDP,HTTP,Socket,TcpClient/UdpClient,HttpClient,WebSocket

.NET TCP、UDP、Socket、WebSocket的更多相关文章

  1. HTTP、TCP、UDP以及SOCKET

    HTTP.TCP.UDP以及SOCKET 一.TCP/IP代表传输控制协议/网际协议,指的是一系列协组. 可分为四个层次:数据链路层.网络层.传输层和应用层. 在网络层:有IP协议.ICMP协议.AR ...

  2. YxdIocp包含有支持大并发的TCP服务组件、HTTP服务组件、UDP服务组件、WebSocket服务组件

    Delphi Windows IOCP 通讯模型封装,基于DIOCP.YxdIocp包含有支持大并发的TCP服务组件.HTTP服务组件.UDP服务组件.WebSocket服务组件,和TCP.UDP等基 ...

  3. [转载]MAC、IP头、TCP头、UDP头帧格式、详解

    原文地址:MAC.IP头.TCP头.UDP头帧格式.详解作者:iTudu 转自:http://zoufengfu168.blog.163.com/blog/static/546105520099133 ...

  4. [转帖]TCP和UDP的135、137、138、139、445端口的作用

    TCP和UDP的135.137.138.139.445端口的作用 https://www.cnblogs.com/IvanChen/p/4500698.html 竟然不知道 端口具体是干什么的.. 如 ...

  5. 37.qt quick- 高仿微信实现局域网聊天V3版本(添加登录界面、UDP校验登录、皮肤更换、3D旋转)

    1.版本介绍(已上传至群里) 版本说明: 添加登录界面. UDP校验登录. 皮肤更换. 3D旋转(主界面和登录界面之间切换) . 效果图如下所示: 如果效果图加载失败,可以去哔哩哔哩 https:// ...

  6. python笔记28(TCP,UDP,socket协议)

    今日内容 1.TCP协议 协议的特点:三次握手,四次挥手: 2.UDP协议 3.OSI七层模型:每层的物理设备,每一层协议. 4.代码部分: ①介绍socket: ②使用socket完成tcp协议的w ...

  7. HTTP、TCP、UDP以及SOCKET之间的区别/联系

    一.TCP/IP代表传输控制协议/网际协议,指的是一系列协组. 可分为四个层次:数据链路层.网络层.传输层和应用层. 在网络层:有IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议. 在 ...

  8. 牛客网Java刷题知识点之TCP、UDP、TCP和UDP的区别、socket、TCP编程的客户端一般步骤、TCP编程的服务器端一般步骤、UDP编程的客户端一般步骤、UDP编程的服务器端一般步骤

    福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑         Java全栈大联盟   ...

  9. HTTP、TCP、UDP,Socket,HTTPS

    TCP   HTTP   UDP三者的关系如下: TCP/IP是个协议组,可分为四个层次:网络接口层.网络层.传输层和应用层. 在网络层有IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协 ...

  10. TCP与UDP在socket编程中的区别

    一.TCP与UDP的区别 基于连接与无连接  对系统资源的要求(TCP较多,UDP少)  UDP程序结构较简单  流模式与数据报模式  TCP保证数据正确性,UDP可能丢包  TCP保证数据顺序,UD ...

随机推荐

  1. StackOverFlow & OutOfMemory

    StackOverFlow & OutOfMemory 两者都为 Error,广义上的"异常" StackOverflow 通常为 Java 虚拟机栈内存不够,JVM 对方 ...

  2. 分布式缓存NewLife.Redis

    NewLife.Redis 是一个Redis客户端组件,以高性能处理大数据实时计算为目标. Redis协议基础实现位于Redis/RedisClient,FullRedis为扩展实现,主要增加列表结构 ...

  3. Vue 页面传参方式 Query 和 Params

    1. query 与 params 传参 query 需要和配合 path 属性使用,携带参数会拼接在请求路径后,效果同 Get 请求方式 http://localhost:8033/Permissi ...

  4. 高精度离线免费 的C#文字识别PaddleOCR库

    随便打开一个Microsoft Visual Studio,新建一个WinForms项目,从下面列表中随便选择一个NET框架.目标平台要设置成X64,该OCR仅支持64位. net35;net40;n ...

  5. itest(爱测试)开源接口测试&敏捷测试管理平台8.1.0发布

    (一)itest 简介 itest 开源敏捷测试管理,testOps 践行者,极简的任务管理,测试管理,缺陷管理,测试环境管理,接口测试,接口Mock 6合1,又有丰富的统计分析.可按测试包分配测试用 ...

  6. kettle从入门到精通 第三十七课 kettle 全量同步(数据量小)

    1.下图是一些常见的数据同步业务场景: 实时数据:对实时性要求很高,延迟在毫秒范围内.常见的有kafka/rabbitmq等消息中间件,mysql binlog日志,oracle归档日志等. 离线数据 ...

  7. nginx resolver 指定多个DNS (2个DNS)

    nginx resolver 指定多个DNS (2个DNS) 直接在 resolver 后边填2个DNS,中间用空格 location / { resolver 223.5.5.5 114.114.1 ...

  8. 修改带有强签名的DLL并重新生成

    一.如果含有强签名,需要先使用去除强签名工具,这里使用的是"StrongNameRemove",点击修正就可以了,如果没有强签名可直接下一步: 二.将没有强签名的DLL进行反编译, ...

  9. php监控

    1.开启php的监控数据监控功能 # 使用部署了php-fpm的机器即可 # yum install php-fpm -y # 1.修改参数 [root@web-7 /etc/php-fpm.d]#g ...

  10. JS 过滤掉两个数组中对象id值相等的项

    const arr1 = [{ id: 1, name: '老二' }]; const arr2 = [{ id: 1, name: '网' }, { id: 2, name: '二位' },{ id ...