首页 > 其他分享 >.NET TCP、UDP、Socket、WebSocket

.NET TCP、UDP、Socket、WebSocket

时间:2024-07-25 11:18:05浏览次数:7  
标签:UDP WebSocket Socket TCP using var new NET

做.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

标签:UDP,WebSocket,Socket,TCP,using,var,new,NET
From: https://www.cnblogs.com/kybs0/p/18312434

相关文章

  • .NET 轻量级 命令行工具 CSharpRepl
    前言当我们需要快速测试代码片段时,常见的做法是启动VisualStudio或使用在线代码编辑器。然而,VisualStudio的启动可能较为缓慢且占用较多系统资源,而在线编辑器则可能遇到语法支持局限或网络延迟问题。为解决这个问题,给大家推荐一款轻量级的本地C#执行工具——CSharpRepl。可以......
  • debian 中使用 telnet 测试服务
    在Debian中使用Telnet命令Telnet是一种基于文本的协议,用于在网络中进行双向交互通信。它通常用于远程登录和测试网络服务。以下是如何在Debian系统上安装和使用Telnet的详细步骤。安装Telnet客户端在Debian系统中,Telnet客户端通常不会预装,因此需要先进行安装。......
  • 将 Streaming Assistants API 与 Websocket 结合使用
    我正在开发一个FastAPI应用程序,其中使用WebSockets从Assistant的API事件处理程序实时发送文本增量。但是,负责发送这些增量的任务仍处于PENDING状态并且永远不会运行。因此,客户端不会收到预期的数据。症状WebSocket连接打开并收到消息。为处理WebSo......
  • 发布.NET应用程序,不单独安装运行时
    很多.NET开发者编译好的的程序,发布到另外一台电脑的时候,往往都不能运行,而是需要安装运行时。这个大多数人都知道,直接去运行时官网下载对应版本的安装包即可。比如我用了6.0,那么选择对应架构的版本下载即可。然后在目标机器上安装这个包,就可以运行我们的.NET程序了。这样操作起来......
  • Java中的WebSocket编程:实时通信实现
    Java中的WebSocket编程:实时通信实现大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!实时通信在现代应用中变得越来越重要,从即时聊天到实时数据更新,WebSocket提供了一种高效的解决方案。本文将详细讲解如何在Java中使用WebSocket进行实时通信,涵盖基本的WebS......
  • 昇思25天学习打卡营第21天|基于MobileNetv2的垃圾分类
    基于MobileNetv2的垃圾分类实验目的MobileNetv2模型原理介绍实验环境数据处理数据准备数据加载数据预处理操作MobileNetV2模型搭建MobileNetV2模型的训练与测试训练策略模型训练与测试模型推理导出AIR/GEIR/ONNX模型文件本文档主要介绍垃圾分类代码开发的方法。通过......
  • 记录荒废了三年的四年.net开发的第二次面试(进复试了)
    这次面试的是小公司,深圳计通智能,面试分为初试和复试。使用腾讯视频会议完成。相比与上次面试,这次有所进步,进复试了。当然,这可能也与面试风格有关。这次面试着重与项目经历和技术,因此回答比较顺畅。这一周干了什么我先是研究了上次面试没回答出来,或者回答得不好的技术问题。然后......
  • 类库项目无法引用Microsoft.AspNetCore程序集下的类库
    类库项目无法引用Microsoft.AspNetCore程序集下的类库 在类库项目中不能直接引用WebApplicationBuilder、ApplicationBuilder等类,这些类位于Microsoft.ASPNetCore程序集中,但是无法通过Nuget包引用,因为该Nuget包的版本已经不再支持,很久没有更新过了。解决方法:在项目文件cspr......
  • .NET Core 核心知识点(四) -- 初会依赖注入
    控制反转、服务定位器、依赖注入  控制反转:使用对象或者服务的时候,不需要自己去创建/new服务,而是在使用的时候直接声明,容器会自动分配一个服务实例。相当于自己用发电机发电使用和利用电网公司的电的区别,自己发电,我需要一台发电机,安装发电机,自己设置电压,频率等等,而使用电......
  • VS2022 安装.NET4.5目标包
    转载自https://www.cnblogs.com/Stay627/p/15549958.html[VS2022安装.NET4.5目标包]众所周知VS2022将不再支持.NET4.5,即使在VisualStudioInstaller中也找不到.NET4.5的选项在不改变项目结构的情况下,要么选择继续使用VS2019,当然博主已经卸掉了,那么还有什么方法呢?我们可以......