Socket Basic Concepts

发布时间:2020-05-30 09:35:34 作者:skydxd
来源:网络 阅读:363

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 API


下面是.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>


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

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

tcp string socket

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

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

相关阅读

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

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