5. 使用TCP

HTTP协议基于传输控制协议(Transmission Control Protocol,TCP)。要使用TCP,客户端首先需要打开一个到服务器的连接,才能发送命令。而使用HTTP,发送文本命令。HttpClient和WebListener类隐藏了HTTP协议的细节。使用TCP类发送HTTP请求时,需要更多地了解HTTP协议。TCP类没有提供用于HTTP协议的功能,必须自己提供。另一方面,TCP类提供了更多的灵活性,因为可以使用这些类与基于TCP的其他协议。

传输控制协议(TCP)类为连接和发送两个端点之间的数据提供了简单的方法。端点是IP地址和端口号的组合。已有的协议很好地定义了端口号,例如,HTTP使用端口80,而SMTP使用端口23。Internet地址编码分配机构(Internet Assigned Numbers Authority,IANA,http://www.iana.org)把端口号赋予这些已知的服务。除非实现某个已知的服务,否则应选择大于1024的端口号。

TCP流量构成了目前Internet上的主要流量。TCP通常是首选的协议,因为它提供了有保证的传输、错误校正和缓冲。TcpClient类封装了TCP连接,提供了许多属性来控制连接,包括缓冲、缓冲区的大小和超时。通过GetStream()方法请求NetworkStream对象可以实现读写功能。

TcpListener类用Start()方法侦听引入的TCP连接。当连接请求到达时,可以使用AcceptSocket()方法返回一个套接字,与远程计算机通信,或使用AcceptTcpClient()方法通过高层的TcpClient对象进行通信。阐明TcpListener类和TcpClient类如何协同工作的最简单方式是给出一个示例。

1. 使用TCP创建HTTP客户程序

首先,创建一个控制台应用程序(包),向Web服务器发送一个HTTP请求。以前用HttpClient类实现了这个功能,但使用TcpClient类需要深入HTTP协议。

HttpClientUisngTcp示例代码使用了以下名称空间:

System

System.IO

System.Net.Sockets

System.Text

System.Threading.Tasks

应用程序接受一个命令行参数,传递服务器的名称。这样,就调用RequestHtmlAsync方法,向服务器发出HTTP请求。它用Task的Result属性返回一个字符串:

        static async Task Main(string[] args)
        {
            if (args.Length != 1)
            {
                ShowUsage();
            }
            Console.WriteLine(await RequestHtmlAsync(args[0]));
            void ShowUsage()
            {
                Console.WriteLine("Usage: HttpClientUsingTcp hostname");
            }
        }

现在看看RequestHtmlAsync方法的最重要部分。首先,实例化一个TcpClient对象。其次,使用ConnectAsync方法,在HTTP默认端口80上建立到主机的TCP连接。再次,通过GetStream方法检索一个流,使用这个连接进行读写:

        public static async Task<string> RequestHtmlAsync(string url)
        {
            try
            {
                Uri uri = new Uri(url);
                Console.WriteLine($"hostName: {uri.Host}, port: {uri.Port}, scheme: {uri.Scheme}\n");
                using (var client = new TcpClient())
                {
                    await client.ConnectAsync(uri.Host,uri.Port);
                    using (NetworkStream stream = client.GetStream())
                    {
...
                    }                   
                }
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

流现在可以用来把请求写到服务器,读取响应。HTTP是一种基于文本的协议,所以很容易在字符串中定义请求。为了向服务器发出一个简单的请求,标题定义了HTTP方法GET,其后是URL/的路径和HTTP版本HTTP/1.1。第二行定义了Host标题、主机名、和端口号,第三行定义了Connection标题。通常,通过Connection标题,客户端请求keep-alive,要求服务器保持连接打开,因为客户端希望发出更多的请求。这里只向服务器发出一个请求,所以服务器应该关闭连接,从而close设置为Connection标题。为了结束标题信息,需要使用\r\n给请求添加一个空行。标题信息调用NetworkStream的方法WriteAsync,用UTF-8编码发送。\r\n为了立即向服务器发送缓冲,请调用FlushAsync方法。否则数据就可能保存在本地缓存:

                    string header = $"GET / {uri.Scheme.ToUpper()}/1.1\r\n" +
                        $"Host: {uri.Host}:{uri.Port}\r\n" +
                        "Connection: close\r\n" +
                        "\r\n";
                    byte[] buffer = Encoding.UTF8.GetBytes(header);
                    await stream.WriteAsync(buffer,0,buffer.Length);
                    await stream.FlushAsync();

现在可以继续这个过程,从服务器中读取回应。不知道回应有多大,所以创建一个动态生长的MemoryStream。使用ReadAsync方法把服务器的回应暂时写入一个字节数组,这个字节数组的内容添加到MemoryStream中。从服务器中读取所有数据后,StreamReader接管控制,把数据从流读入一个字符串,并返回给调用者:

                    var ms = new MemoryStream();
                    buffer = new byte[ReadBufferSize];
                    int read = 0;
                    do
                    {
                        read = await stream.ReadAsync(buffer, 0, ReadBufferSize);
                        await ms.WriteAsync(buffer, 0, read);
                        Array.Clear(buffer, 0, buffer.Length);
                    } while (read > 0);
                    ms.Seek(0, SeekOrigin.Begin);
                    using (var reader = new StreamReader(ms))
                    {
                        return await reader.ReadToEndAsync();
                    }

把一个http协议网站字符串传递给程序,会看到一个成功的请求,其HTML内容显示在控制台上。(即使是http协议,也并非所有网站都请求成功。)

传递网站http://services.odata.org/Northwind/Northwind.svc/Regions,输出结果如下:

hostName: services.odata.org, port: 80, scheme: http

HTTP/1.1 301 Moved Permanently
Content-Type: text/html; charset=UTF-8
Location: https://services.odata.org:80/
Server: Microsoft-IIS/10.0
Date: Thu, 11 Jun 2020 17:19:36 GMT
Connection: close
Content-Length: 0

传递 网站http://yyets.cc/index.php/samples,输出结果如下:

hostName: yyets.cc, port: 80, scheme: http

HTTP/1.1 200 OK
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization, Accept-Encoding, UserAccount
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization, Accept-Encoding, UserAccount
Server: nginx
Date: Thu, 11 Jun 2020 17:40:58 GMT
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
X-Powered-By: PHP/7.0.33
X-Cache: MISS from zz-jp01
X-Cache: MISS from asia-hk30
Transfer-Encoding: chunked
Connection: close

a10
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8" />
    <title>YYeTs.cc新人人影视_电影下载网_最新电影_迅雷高清电影下载</title>
    <meta name="keywords" content="人人影视,YYeTs,迅雷电影,最新连续剧,好看的高清电影" />
        <meta name="description" content="新人人影视-YYeTs.cc是一个免费提供迅雷电影及韩剧,日剧,美剧等电视剧迅雷下载,磁力链,BT种子等下载方式. 在线观看的网站,资源全部来自互联网及网友p2p资源." />
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
    <link type="image/vnd.microsoft.icon" href="/favicon.png" rel="shortcut icon" />
    <link href="/template/km66/css/app.css" rel="stylesheet" type="text/css" />
    <script src="/template/km66/js/jquery.min.js"></script&g
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
net.ipv4.tcp_keepalive_time、net.ipv4.tcp_keepalive_intvl和net.ipv4.tcp_keepalive_probes这三个参数都是用于控制TCP的keepalive机制的参数。它们的具体含义如下: 1. net.ipv4.tcp_keepalive_time:该参数定义了TCP keepalive探测报文的发送间隔,单位为秒。当一个TCP连接空闲时间超过该参数设定的值时,内核会发送一个keepalive探测报文给对端。如果对端在一定时间内没有回应,则认为连接已经断开。该参数的默认值为7200秒(2小时)。 2. net.ipv4.tcp_keepalive_intvl:该参数定义了TCP keepalive探测报文的发送间隔,单位为秒。当一次keepalive探测报文未获得对端回应时,内核会继续发送下一次探测报文,直到达到一定次数或连接断开。该参数的默认值为75秒。 3. net.ipv4.tcp_keepalive_probes:该参数定义了TCP keepalive探测报文的最大发送次数。当一个TCP连接空闲时间超过net.ipv4.tcp_keepalive_time设定的值时,内核会发送一次keepalive探测报文给对端,如果对端未回应,则继续发送下一次探测报文,直到达到该参数设定的最大次数或连接断开。该参数的默认值为9次。 需要注意的是,这三个参数的取值应该根据具体的应用场景和网络状况进行调整,以达到最佳的效果。一般来说,建议将net.ipv4.tcp_keepalive_time设置为较小的值,如60秒或120秒,将net.ipv4.tcp_keepalive_intvl设置为较小的值,如30秒或60秒,将net.ipv4.tcp_keepalive_probes设置为较小的值,如3次或5次,以提高连接的可靠性。但是,具体取值还需要根据实际情况进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值