Socket Basic Concepts

发布时间:2020-05-30 09:35:34 作者:skydxd
阅读:384
开发者专用服务器限时活动,0元免费领! 查看>>

Socket Basic Concepts


Socket Basic Concepts 

首先介绍Socket的一些基本概念

Socket是操作系统提供的一系列网络编程接口。

网络模型分若干层,也有一些协议,比如TCP协议,UDP协议等,这些都是抽象的定义,在硬件以及操作系统级别上有一些对应的实现,Socket可以看做操作系统为开发人员提供的一系列网络编程接口,它封装了一些协议的细节,比如怎么组织数据包,怎么发送数据之类的。

Socket编程的几个基本概念 

Endpoint
Endpoin指定要连接到哪里,Endpoint包括两部分内容,IP和Port,IP地址和端口组合起来才能唯一指定远程的通信端。

AddressFamily
怎么寻址,有了IP地址之后就是如何寻址的问题,常用的寻址方案是IP V4和IP V6两种类型,windows操作系统从VISTA和Windows 20008起默认支持IPV6。

Protocol
使用什么协议进行通信,比如TCP协议或者UDP协议,下面介绍Socket类型的时候还会涉及TCP和UDP等协议的介绍。

Socket类型

Socket有三种常用类型:Stream, Dgram, Raw

Stream流类型,支持可靠、双向、基于连接的字节流,使用TCP协议。

Dgram数据报类型,支持数据报,即最大长度固定的无连接、不可靠消息。消息可能会丢失或重复并可能在到达时不按顺序排列,使用UDP协议。

Raw类型支持对基础传输协议的访问,需要自己生成数据包。网上有一些RAW的例子,比如D.O.S***,ARP***,网络监控之类的。

本文只讨论Stream类型的Socket编程,RAW和Dgram不在讨论之列,也就是只讨论基于TCP协议的编程。

一些常见的概念问题

Socket和TCP/IP有什么关系?

Socket和TCP/IP不是一个层面的概念,Socket是操作系统提供的操作TCP数据的编程接口。

Sockets V4、Sockets V5有什么区别?

经常看到一些软件可以设置Sockets4/Sockets5代理,简单说他们是客户端与外网服务器之间通讯的协议,Sockets是位于应用层与传输层之间的中间层。 Sockets V4支持TCP, Sockets V5支持TCP/UDP,支持安全认证,支持IPV6。

Socket能够同时接受和发送数据吗?

TCP协议是双工的

Socket如何保证数据按顺序到达?

TCP协议来保证

Socket的基本通信模型模型

客户端:

Socket()
Connect
Send
Close

服务器端:

Socket()
Bind
Listen
Accept
Receive
Send
Close

客户端和服务器端模型是不一样的,两边是非对称的。


下面是.Net Socket编程最基本的几个类,位于命名空间System.Net.Sockets

Socket Socket接口类
TcpClient TCP客户端类
TcpListener TCP侦听类
NetworkStream 用于网络访问的基础数据流

其他经常用到的辅助类,位于命名空间System.Net

Dns 域名解析
EndPoint  标识网络地址
IPAddress  IP地址。
NetworkCredential 基于密码的身份验证方案,不支持基于公钥的身份验证方法(比如ssl)

一个Socket的简单例子

输入网址,获得HTML页面的一段演示代码,只是演示Socket对象的几个主要功能,不具有实用价值。

基本步骤为:建立Socket对象,连接服务器,发送数据,然后接受数据,对应上一章介绍的Socket通信模型。

代码

01 <span style="font-size: 10pt;">private string DownloadPage(string path)
02         {
03             Uri uri = new Uri(path);
04             Encoding encoding = Encoding.UTF8;// .GetEncoding("gb2312");
05  
06             string requestHeader = BuildRequestHeader(uri);
07             byte[] requestBytes = encoding.GetBytes(requestHeader);
08             byte[] receivedBytes = new byte[1024 * 100];
09  
10             Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
11             socket.Connect(uri.Host,uri.Port);
12             socket.Send(requestBytes);
13  
14             int receivedBytesLength = socket.Receive(receivedBytes);
15             socket.Shutdown(SocketShutdown.Both);
16             socket.Close();
17  
18             string html = string.Empty;
19             if (receivedBytesLength &gt; 0)
20             {
21                 html = encoding.GetString(receivedBytes, 0, receivedBytesLength);
22             }
23             return html;
24         }</span>


构造HTTP Header的代码如下,注意不要忘了Http头模板的最后一行

01 <span style="font-size: 10pt;">private string BuildRequestHeader(Uri uri)
02         {
03             string httpHeaderTemplate = @"GET {url} HTTP/1.1
04 Connection: Close
05 Host: {host}
06  
07 ";
08             return httpHeaderTemplate.Replace("{url}", uri.AbsolutePath)
09                 .Replace("{host}", uri.Host);
10  
11         }</span>


30秒思考题:这段简单代码有什么问题?

我们定义的用来接收数据的数组大小是固定的,如果要接受的数据超过数组大小怎么办? 可以定义一个缓冲区,每次接受固定大小的数据,直到接收完成为止。示例代码如下:

01 <span style="font-size: 10pt;">MemoryStream ms = new MemoryStream();
02             while (true)
03             {
04                 Console.WriteLine("Available :{0}", socket.Available);
05                 int receivedBytesLength = socket.Receive(receivedBytes, 0, receivedBytes.Length, SocketFlags.None);
06                 if (receivedBytesLength &gt; 0)
07                 {
08                     ms.Write(receivedBytes, 0, receivedBytesLength);
09                 }
10                 else
11                 {
12                     break;
13                 }
14             }
15  
16             string html = string.Empty;
17             if (ms.Length &gt; 0)
18             {
19                 html = encoding.GetString(ms.ToArray(), 0, (int)ms.Length);
20             }</span>


Socket的缓冲区

Socket接收数据时,操作系统先把数据接收到缓冲区,然后通知程序,socket.Available 是从已经从网络接收的、可供读取的数据的字节数,这个值是指缓冲区中已接收数据的字节数,不是实际的数据大小。而且如果网络有延迟,Send之后马上读取Available属性不一定能读到正确的值,所以不能利用socket.Available来判断总共要接受的字节数。

在上面的方法中,如果没有可读取的数据,则 Receive 方法将一直处于阻止状态,直到有数据可用,如果Server端也没有正确关闭连接,程序很容易死在这里,可以通过Socket.ReceiveTimeout来设置Socket对象接受数据的超时时间。

30秒思考题:为什么这样下载的页面有时候和浏览器下载的页面不一样?
>>gzip,chunked编码,重定向等

NetworkStream的例子

前面讲过基于TCP协议的Socket是Steam类型的,在操作系统中,为了简化编程,把设备、文件等都看作流对象,统一编程接口。NetworkStream类提供了在阻止模式下通过Socket套接字发送和接收数据的方法,.Net还提供了TcpClient和TcpListener类,用于简化同步阻止模式下通过TCP协议连接、发送和接收流数据。下面的例子是这几个对象的简单介绍,省略了一些细节,也不具有实用价值。

这个例子模拟计算机远程控制,先新起一个线程模拟服务进程,在这个线程中创建一个TcpListener对象,等待客户端连接。用户在客户端界面点了“连接”按钮后,UI线程创建TcpClient对象,等待用户输入dos命令,用户输入dos命令,按执行按钮,这时TcpClient对象把用户输入的命令发送给TcpListener对象,服务进程执行完命令后,将执行结果反馈给TcpClient对象。

部分代码。
       

01 <span style="font-size: 10pt;">private void StartServer()
02         {
03             TcpListener server = new TcpListener(IPAddress.Any, 10000);
04             server.Start();
05             Debug.WriteLine("Server: start");
06  
07             TcpClient client = server.AcceptTcpClient();
08             Debug.WriteLine("Server : connection accept");
09             NetworkStream stream = client.GetStream();
10  
11             Process process = new Process();
12             process.StartInfo.FileName = "cmd.exe";
13             process.StartInfo.UseShellExecute = false;
14             process.StartInfo.RedirectStandardInput = true;
15             process.StartInfo.RedirectStandardOutput = true;
16             process.StartInfo.RedirectStandardError = true;
17             process.StartInfo.CreateNoWindow = false;
18             process.Start();
19  
20             process.OutputDataReceived += (Object sender, DataReceivedEventArgs e) =>
21             {
22                 byte[] bytes = Encoding.GetEncoding("gb2312").GetBytes(e.Data + "\r\n");
23                 stream.Write(bytes, 0, bytes.Length);
24             };
25             process.BeginOutputReadLine();
26              
27             while (true)
28             {
29                 Byte[] buffer = new Byte[1024 * 10];
30                 int length = stream.Read(buffer, 0, buffer.Length);
31                 if (length == 0)
32                 {
33                     Debug.WriteLine("Server: read 0 byte");
34                     break;
35                 }
36  
37                 string command = Encoding.GetEncoding("gb2312").GetString(buffer, 0, length);
38                 Debug.WriteLine("Server: receive {0} ", command);
39  
40                 StreamWriter Writer = process.StandardInput;
41                 Writer.WriteLine(command);
42                 Writer.Flush();
43             }
44  
45             server.Stop();
46             process.WaitForExit(1000);
47             process.Close();
48             Debug.WriteLine("Server: close");
49         }
50  
51         TcpClient client;
52         private void ConnectButton_Click(object sender, EventArgs e)
53         {
54             client = new TcpClient();
55             client.Connect("localhost", 10000);
56             Console.WriteLine("Client: connect");
57             StartButton.Enabled = true;
58         }
59  
60         private void StartButton_Click(object sender, EventArgs e)
61         {
62             if (CommandTextBox.Text.Trim().Length == 0)
63             {
64                 return;
65             }
66  
67             string request = CommandTextBox.Text.Trim();
68             byte[] bytes = Encoding.GetEncoding("gb2312").GetBytes(request);
69             NetworkStream stream = client.GetStream();
70             stream.Write(bytes, 0, bytes.Length);
71              
72             Console.WriteLine("Client: request {0}", request);
73             MessageTextBox.AppendText("\r\nresponse from server:\r\n");
74              
75             byte[] buffer = new byte[1024];
76             do{
77                 int receivedBytesLength = stream.Read(buffer, 0, buffer.Length);
78                 if(receivedBytesLength > 0)
79                 {
80                     string text = Encoding.GetEncoding("gb2312").GetString(buffer, 0, receivedBytesLength);
81                     MessageTextBox.AppendText(text);
82                     MessageTextBox.AppendText("\r\n");
83                 }
84                 else
85                 {
86                     break;
87                 }
88             }while(stream.DataAvailable);
89         }</span>


亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

推荐阅读:
  1. 详解小白之KMP算法及python实现
  2. forEach与map方法在JavaScript中的区别是什么

开发者交流群:

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

tcp string socket

上一篇:Juniper ex4200 端口镜像问题

下一篇:掌握系列之微服务-1.概念

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》
开发者交流群×