.NET TCP、UDP、Socket、WebSocket

打印 上一主题 下一主题

主题 860|帖子 860|积分 2582

做.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:
  1.     //创建一个 Socket 实例
  2.     Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  3.    
  4.     //连接到服务器
  5.     clientSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8000));
  6.    
  7.     //发送数据
  8.     string message = "Hello, Server!";
  9.     byte[] data = Encoding.ASCII.GetBytes(message);
  10.     clientSocket.Send(data);
  11.    
  12.     //接收数据
  13.     byte[] buffer = new byte[1024];
  14.     int bytesRead = clientSocket.Receive(buffer);
  15.     Debug.WriteLine(Encoding.ASCII.GetString(buffer, 0, bytesRead));
  16.    
  17.     clientSocket.Close();<br>
复制代码
TcpClient/UdpClient

TCP/UDP均是位于传输层的通讯协议,以是Socket的利用也是位于传输层的通讯操作
TCP是面向连接,提供可靠、顺序的数据流传输。用于一对一的通讯,即一个TCP连接只能有一个发送方和一个接收方。详细连接方式是,先通过三次握手创建连接、然后传输数据,传输数据完再通过4次挥手关闭连接。以是适用于需要数据完整性和可靠传输的场景
而UDP则是无连接的,不需要创建和维护连接状态,不提供确认机制,也不重传丢失的数据报,但也因此传输实时性高,适合低延时、数据量小、广播场景
基于Socket抽象编程接口,TCP、UDP构建更高级别抽象网络编程TcpClient、UdpClient,它们用于简化TCP网络编程中的常见任务
TcpClient、UdpClient是 .NET 提供的用于方便管理TCP和UDP网络通讯的类,下面是对应的Demo
Tcp服务端:
  1. 1 using System;
  2. 2 using System.Net;
  3. 3 using System.Net.Sockets;
  4. 4 using System.Text;
  5. 5
  6. 6 class TcpServerExample
  7. 7 {
  8. 8     public static void Main()
  9. 9     {
  10. 10         TcpListener listener = new TcpListener(“127.0.0.1", 8000);
  11. 11         listener.Start();
  12. 12         Console.WriteLine("Server is listening on port 8000...");
  13. 13
  14. 14         TcpClient client = listener.AcceptTcpClient();
  15. 15         NetworkStream stream = client.GetStream();
  16. 16         
  17. 17         byte[] data = new byte[1024];
  18. 18         int bytesRead = stream.Read(data, 0, data.Length);
  19. 19         Console.WriteLine("Received: " + Encoding.ASCII.GetString(data, 0, bytesRead));
  20. 20
  21. 21         byte[] response = Encoding.ASCII.GetBytes("Hello, Client!");
  22. 22         stream.Write(response, 0, response.Length);
  23. 23
  24. 24         stream.Close();
  25. 25         client.Close();
  26. 26         listener.Stop();
  27. 27     }
  28. 28 }
复制代码
TCP客户端:
  1. 1 using System;
  2. 2 using System.Net.Sockets;
  3. 3 using System.Text;
  4. 4
  5. 5 class TcpClientExample
  6. 6 {
  7. 7     public static void Main()
  8. 8     {
  9. 9         TcpClient client = new TcpClient("127.0.0.1", 8000);
  10. 10         NetworkStream stream = client.GetStream();
  11. 11
  12. 12         byte[] message = Encoding.ASCII.GetBytes("Hello, Server!");
  13. 13         stream.Write(message, 0, message.Length);
  14. 14
  15. 15         byte[] data = new byte[1024];
  16. 16         int bytesRead = stream.Read(data, 0, data.Length);
  17. 17         Debug.WriteLine("Received: " + Encoding.ASCII.GetString(data, 0, bytesRead));
  18. 18
  19. 19         stream.Close();
  20. 20         client.Close();
  21. 21     }
  22. 22 }
复制代码
Udp服务端:
  1. 1 using System;
  2. 2 using System.Net;
  3. 3 using System.Net.Sockets;
  4. 4 using System.Text;
  5. 5
  6. 6 class UdpServerExample
  7. 7 {
  8. 8     public static void Main()
  9. 9     {
  10. 10         UdpClient udpServer = new UdpClient(8000);
  11. 11         IPEndPoint remoteEP = new IPEndPoint(”127.0.0.1“, 0);
  12. 12
  13. 13         Console.WriteLine("Server is listening on port 8000...");
  14. 14
  15. 15         byte[] data = udpServer.Receive(ref remoteEP);
  16. 16         Console.WriteLine("Received: " + Encoding.ASCII.GetString(data));
  17. 17
  18. 18         byte[] response = Encoding.ASCII.GetBytes("Hello, Client!");
  19. 19         udpServer.Send(response, response.Length, remoteEP);
  20. 20
  21. 21         udpServer.Close();
  22. 22     }
  23. 23 }
复制代码
Udp客户端:
  1. 1 using System;
  2. 2 using System.Net;
  3. 3 using System.Net.Sockets;
  4. 4 using System.Text;
  5. 5
  6. 6 class UdpClientExample
  7. 7 {
  8. 8     public static void Main()
  9. 9     {
  10. 10         UdpClient udpClient = new UdpClient();
  11. 11         IPEndPoint remoteEP = new IPEndPoint(”127.0.0.1", 8000);
  12. 12
  13. 13         byte[] message = Encoding.ASCII.GetBytes("Hello, Server!");
  14. 14         udpClient.Send(message, message.Length, remoteEP);
  15. 15
  16. 16         byte[] data = udpClient.Receive(ref remoteEP);
  17. 17         Console.WriteLine("Received: " + Encoding.ASCII.GetString(data));
  18. 18
  19. 19         udpClient.Close();
  20. 20     }
  21. 21 }
复制代码
上面是根本的网络通讯DEMO,TcpClient用于基于连接、可靠的TCP通讯,适用于需要数据完整性和可靠传输的场景。Udp用于无连接、不保证传输的UDP通讯,适用于对实时性要求高、允许少量数据丢失的场景(如视频流)。会议场景下的传屏软件适适用这个协议,传屏发送端固定帧率一直推送,网络丢失几帧问题不大,重要的是延时低了很多。
TcpClient、UdpClient是位于传输层的通讯类,分别实现了基于TCP和UDP协议的通讯功能。
HttpClient
讲完传输层的网络通讯类,就要说下应用层的HttpClient,这是专门用于HTTP协议的通讯
Http与TCP/UDP均是网络通讯协议,TCP、UDP位于传输层,HTTP传于应用层,而且HTTP是基于TCP面向连接的。TCPHTTP1.1之后引入长期连接,允许一个TCP连接举行多次请求/响应传输。HTTP层相比TCP它关注请求、响应的内容
HttpClient是Http协议的通讯类,提供了封装好的、高级的HTTP功能(如发起GET, POST请求,处理惩罚响应等)。
HttpClient可以用于Web接口如Restful API的调用,我这边Windows应用的WebApi基础组件库就是用HttpClient实现的。
HttpClient类,在System.Net.Http.HttpClient命名空间下,HttpClient的内部实现是基于Socket的。也就是说,HttpClient底层利用Socket接口来创建连接并传输数据,但它隐藏了这些细节,为开发者提供了一个更简洁的API。
下面是我基于HttpClient实现的Web服务各类操作入口代码,可以简朴浏览下:
  1. 1         /// <summary>
  2. 2         /// 请求/推送数据
  3. 3         /// </summary>
  4. 4         /// <typeparam name="TResponse"></typeparam>
  5. 5         /// <param name="request"></param>
  6. 6         /// <returns></returns>
  7. 7         public async Task<TResponse> RequestAsync<TResponse>(HttpRequest request) where TResponse : HttpResponse, new()
  8. 8         {
  9. 9             var requestUrl = request.GetRequestUrl();
  10. 10             try
  11. 11             {
  12. 12                 using var client = CreateHttpClient(request);
  13. 13                 var requestMethod = request.GetRequestMethod();
  14. 14                 switch (requestMethod)
  15. 15                 {
  16. 16                     case RequestMethod.Get:
  17. 17                         {
  18. 18                             using var response = await client.GetAsync(requestUrl);
  19. 19                             return await response.GetTResponseAsync<TResponse>();
  20. 20                         }
  21. 21                     case RequestMethod.Post:
  22. 22                         {
  23. 23                             using var httpContent = request.GetHttpContent();
  24. 24                             using var response = await client.PostAsync(requestUrl, httpContent);
  25. 25                             return await response.GetTResponseAsync<TResponse>();
  26. 26                         }
  27. 27                     case RequestMethod.Put:
  28. 28                         {
  29. 29                             using var httpContent = request.GetHttpContent();
  30. 30                             using var response = await client.PutAsync(requestUrl, httpContent);
  31. 31                             return await response.GetTResponseAsync<TResponse>();
  32. 32                         }
  33. 33                     case RequestMethod.Delete:
  34. 34                         {
  35. 35                             using var response = await client.DeleteAsync(requestUrl);
  36. 36                             return await response.GetTResponseAsync<TResponse>();
  37. 37                         }
  38. 38                     case RequestMethod.PostForm:
  39. 39                         {
  40. 40                             using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrl);
  41. 41                             using var httpContent = request.GetHttpContent();
  42. 42                             requestMessage.Content = httpContent;
  43. 43                             using var response = await client.SendAsync(requestMessage);
  44. 44                             return await response.GetTResponseAsync<TResponse>();
  45. 45                         }
  46. 46                 }
  47. 47                 return new TResponse() { Message = $"不支持的请求类型:{requestMethod}" };
  48. 48             }
  49. 49             catch (ArgumentNullException e)
  50. 50             {
  51. 51                 return new TResponse() { Code = NetErrorCodes.ParameterError, Message = e.Message, JsonData = e.StackTrace };
  52. 52             }
  53. 53             catch (TimeoutException e)
  54. 54             {
  55. 55                 return new TResponse() { Code = NetErrorCodes.TimeOut, Message = e.Message, JsonData = e.StackTrace };
  56. 56             }
  57. 57             catch (Exception e)
  58. 58             {
  59. 59                 return new TResponse() { Message = e.Message, JsonData = e.StackTrace };
  60. 60             }
  61. 61         }
复制代码
HttpClient封装后的网络基础组件调用方式,也比较简朴。
添加接口请求说明,参数及请求参数均统一在一个类文件里定义好:
  1. 1 /// <summary>
  2. 2 /// 内网穿透注册接口
  3. 3 /// </summary>
  4. 4 [Request("http://frp.supporter.ws.h3c.com/user/register",RequestMethod.Post)]
  5. 5 [DataContract]
  6. 6 internal class RegisterFrpRequest : HttpRequest
  7. 7 {
  8. 8     public RegisterFrpRequest(string sn, string appName)
  9. 9     {
  10. 10         Sn = sn;
  11. 11         SeverNames = new List<RequestServiceName>()
  12. 12         {
  13. 13             new RequestServiceName(appName,"http")
  14. 14         };
  15. 15     }
  16. 16     [DataMember(Name = "sn")]
  17. 17     public string Sn { get; set; }
  18. 18
  19. 19     [DataMember(Name = "localServerNames")]
  20. 20     public List<RequestServiceName> SeverNames { get; set; }
  21. 21 }
复制代码
再定义请求结果返回数据,基类HttpResponse内有定义根本参数,状态Success、状态码Code、返回描述信息Message:
  1. 1 [DataContract]
  2. 2 class RegisterFrpResponse : HttpResponse
  3. 3 {
  4. 4
  5. 5     [DataMember(Name = "correlationId")]
  6. 6     public string CorrelationId { get; set; }
  7. 7
  8. 8     [DataMember(Name = "data")]
  9. 9     public FrpRegisterData Data { get; set; }
  10. 10
  11. 11     /// <summary>
  12. 12     /// 是否成功
  13. 13     /// </summary>
  14. 14     public bool IsSuccess => Success && Code == 200000 && Data != null;
  15. 15 }
复制代码
然后,业务层可以举行简洁、高效率的调用:
var netClient = new NetHttpClient();
var response = await netClient.RequestAsync(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. 1     /// <summary>
  2. 2     /// 发送消息
  3. 3     /// </summary>
  4. 4     /// <typeparam name="TInput">发送参数类型</typeparam>
  5. 5     /// <param name="client">目标客户端</param>
  6. 6     /// <param name="innerEvent">事件名</param>
  7. 7     /// <param name="data">发送参数</param>
  8. 8     /// <returns></returns>
  9. 9     public async Task<ClientResponse> SendAsync<TInput>(string client, InnerEventItem innerEvent, TInput data)
  10. 10     {
  11. 11         var message = new ChannelSendingMessage(client, new ClientEvent(innerEvent.EventName, innerEvent.EventId, true), _sourceClient);
  12. 12         message.SetData<TInput>(data);
  13. 13         return await SendMessageAsync(ChannelMessageType.ClientCommunication, message);
  14. 14     }
  15. 15
  16. 16     /// <summary>
  17. 17     /// 订阅消息
  18. 18     /// </summary>
  19. 19     /// <param name="client">目标客户端</param>
  20. 20     /// <param name="innerEvent">事件名称</param>
  21. 21     /// <param name="func">委托</param>
  22. 22     public ClientSubscribedEvent SubscribeFunc(string client, InnerEventItem innerEvent, Func<ClientResponse, object> func)
  23. 23     {
  24. 24         var eventName = innerEvent?.EventName;
  25. 25         if (string.IsNullOrEmpty(eventName) || func == null)
  26. 26         {
  27. 27             throw new ArgumentNullException($"{nameof(eventName)}或{nameof(func)},参数不能为空!");
  28. 28         }
  29. 29
  30. 30         var subscribedEvent = new ClientSubscribedEvent(client, innerEvent, func);
  31. 31         SubscribeEvent(subscribedEvent);
  32. 32         return subscribedEvent;
  33. 33     }
  34. 34     /// <summary>
  35. 35     /// 订阅消息
  36. 36     /// </summary>
  37. 37     /// <param name="client">目标客户端</param>
  38. 38     /// <param name="innerEvent">事件名称</param>
  39. 39     /// <param name="func">委托</param>
  40. 40     public ClientSubscribedEvent SubscribeFuncTask(string client, InnerEventItem innerEvent, Func<ClientResponse, Task<object>> func)
  41. 41     {
  42. 42         var eventName = innerEvent?.EventName;
  43. 43         if (string.IsNullOrEmpty(eventName) || func == null)
  44. 44         {
  45. 45             throw new ArgumentNullException($"{nameof(eventName)}或{nameof(func)},参数不能为空!");
  46. 46         }
  47. 47
  48. 48         var subscribedEvent = new ClientSubscribedEvent(client, innerEvent, func);
  49. 49         SubscribeEvent(subscribedEvent);
  50. 50         return subscribedEvent;
  51. 51     }
  52. 52
  53. 53     /// <summary>
  54. 54     /// 订阅消息
  55. 55     /// </summary>
  56. 56     /// <param name="client">目标客户端</param>
  57. 57     /// <param name="innerEvent">事件名称</param>
  58. 58     /// <param name="action">委托</param>
  59. 59     public ClientSubscribedEvent Subscribe(string client, InnerEventItem innerEvent, Action<ClientResponse> action)
  60. 60     {
  61. 61         var eventName = innerEvent?.EventName;
  62. 62         if (string.IsNullOrEmpty(eventName) || action == null)
  63. 63         {
  64. 64             throw new ArgumentNullException($"{nameof(eventName)}或{nameof(action)},参数不能为空!");
  65. 65         }
  66. 66
  67. 67         var subscribedEvent = new ClientSubscribedEvent(client, innerEvent, action);
  68. 68         SubscribeEvent(subscribedEvent);
  69. 69         return subscribedEvent;
  70. 70     }
复制代码
关键词:TCP/UDP,HTTP,Socket,TcpClient/UdpClient,HttpClient,WebSocket
出处:http://www.cnblogs.com/kybs0/本文版权归作者和博客园共有,接待转载,但未经作者同意必须在文章页面给出原文连接,否则生存追究法律责任的权利。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用多少眼泪才能让你相信

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表