C#中使用Socket请求Web服务器过程
最开始我们需要明白一件事情,因为这是这篇文章的前提:
HTTP协议只是一个应用层协议,它底层是通过TCP进行传输数据的。因此,浏览器访问Web服务器的过程必须先有“连接建立”的发生。
而有人或许会问:众所周知,HTTP协议有两大特性,一个是“无连接”性,一个是“无状态”性。这里的“无连接”岂不是跟上面的说法有冲突?其实这里并没有矛盾,
只是人们对“连接”这个词的理解有差异。首先我们来看一下浏览器向Web服务器发出Http请求以及Web服务器给浏览器回复的过程:
- 浏览器创建Socket,按给定IP(域名)和端口(默认为80)连接服务器。比如使用类似Socket.Connect()、Socket.BeginConnect()等方法;
- 连接成功后,浏览器依据HTTP协议规范(关于协议,后面有讲到),向Web服务器发送请求数据。比如“请求行”、“请求头标”以及“请求数据”等,这里可能使用类似Socket.Send()、Socket.BeginSend()等方法。【关于HTTP协议中的请求行、请求头标等请参见http://www.cnblogs.com/riky/archive/2007/04/09/705848.html】
- 浏览器等待服务器处理并返回数据;
- Web服务器端使用Socket.Accept()、Socket.BeginAccept()等方法侦听到浏览器的连接后,便开始接收浏览器发送的数据。接收到请求数据后,依据HTTP协议规范解析数据,然后处理,最终将处理结果(如html文档)发回给浏览器,这里可能用到类似Socket.Send()、Socket.BeginSend()等方法;
- Web服务器发送完处理结果后,关闭Socket;
- 浏览器接收Web服务器发回的数据(如html),将其显示在浏览器UI界面。关闭socket;
- 一次“浏览器到Web服务器”的http请求结束;
- 下一次浏览器需要请求Web服务器,跳转到第1)步循环开始。
如上图所示。浏览器向Web服务器发送http请求之前,需要先建立连接。没错,它们间建立连接的过程跟我们平时开发socket程序类似。由此可知,
HTTP协议的“无连接”特性并不是指:浏览器与Web服务器进行数据交换时,不需要建立连接。那么“无连接”特性到底指什么呢?我们再看图1会发现,
浏览器每次请求完毕后都会与服务器处于“断开”状态,下一次请求时再重新与服务器建立连接。HTTP的无连接特性恰恰就是指浏览器的每次请求都必须重新与服务器建立连接,
正常情况下,浏览器不会与Web服务器保持长时间的连接状态。现将HTTP协议的两大特性归结如下:
以下是发送HTTP请求的代码:
/// <summary> 2 /// 发送请求 3 /// </summary> 4 /// <param name="socket"></param> 5 private void SendRequest(Socket socket) 6 { 7 string h1 = "GET " + _path + " HTTP/1.1\r\n"; 8 string h2 = "Accept: */*\r\n"; 9 string h3 = "Accept-Language: zh-cn\r\n"; 10 string h4 = "Host: " + _host + "\r\n"; 11 string h5 = "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36\r\n"; 12 string h7 = "Connection: close\r\n\r\n"; 13 14 byte[] send_buffer = Encoding.UTF8.GetBytes(h1 + h2 + h3 + h4 + h5 + h7); 15 socket.Send(send_buffer); 16 Print("请求发送完毕,等待Web Server回复..."); 17 socket.BeginReceive(_buffer, 0, 640 * 1024, SocketFlags.None, new AsyncCallback(OnReceive), socket); 18 }
源码下载:https://files.cnblogs.com/xiaozhi_5638/socket_browser.rar
作者:周见智 出处:http://www.cnblogs.com/xiaozhi_5638/ 首发公众号,扫描二维码关注公众号,分享原创计算机视觉/深度学习/算法落地相关文章 来源:https://www.cnblogs.com/xiaozhi_5638/p/3912668.html
public partial class Form1 : Form { Socket _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //客户端socket byte[] _buffer = new byte[1024 * 640]; //接收缓冲区 IPAddress _ip; //当前请求主机IP int _port; //当前请求主机Port string _path; //当前请求url(除去主机部分) string _host; //当前请求url(主机部分) bool _isBusy = false; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } /// <summary> /// 访问 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { if (_isBusy) return; if (textBox2.Text != "") { _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _isBusy = true; Uri u = new Uri(textBox2.Text); _ip = Dns.GetHostByName(u.Host).AddressList[0]; _port = u.Port; _path = u.AbsolutePath; _host = u.Authority; Print("开始连接Web Server..."); Print("Web Server信息:【" + "IP:" + _ip.ToString() + " 端口:" + _port + "】"); _socket.BeginConnect(new IPEndPoint(_ip, _port), new AsyncCallback(OnConnect), _socket); //使用socket连接web server } } /// <summary> /// 异步连接 回调 /// </summary> /// <param name="ar"></param> private void OnConnect(IAsyncResult ar) { Socket socket = ar.AsyncState as Socket; try { socket.EndConnect(ar); Print("连接Web Server完成, 本地socket端口:"+ (_socket.LocalEndPoint as IPEndPoint).Port); Print("开始向Web Server请求数据..."); SendRequest(socket); } catch { Print("连接Web Server失败!"); _isBusy = false; } } /// <summary> /// 异步接收数据 回调 /// </summary> /// <param name="ar"></param> private void OnReceive(IAsyncResult ar) { Socket socket = ar.AsyncState as Socket; try { int real_recv = socket.EndReceive(ar); string recv_str = Encoding.UTF8.GetString(_buffer, 0, real_recv); //第一次接收数据 while ((real_recv = socket.Receive(_buffer)) != 0) //循环接收剩余数据 { recv_str += Encoding.UTF8.GetString(_buffer, 0, real_recv); } Print("收到Web Server的回复:"); Print("\r\n"+recv_str); //输出到界面 } catch { } finally { _isBusy = false; _socket.Close(); } } /// <summary> /// 发送请求 /// </summary> /// <param name="socket"></param> private void SendRequest(Socket socket) { string h1 = "GET" + _path + " HTTP/1.1\r\n"; string h2 = "Accept: */*\r\n"; string h3 = "Accept-Language: zh-cn\r\n"; string h4 = "Host: " + _host + "\r\n"; string h5 = "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36\r\n"; string h7 = "Connection: close\r\n\r\n"; byte[] send_buffer = Encoding.UTF8.GetBytes(h1 + h2 + h3 + h4 + h5 + h7); socket.Send(send_buffer); Print("请求发送完毕,等待Web Server回复..."); socket.BeginReceive(_buffer, 0, 640 * 1024, SocketFlags.None, new AsyncCallback(OnReceive), socket); } /// <summary> /// 输出信息 /// </summary> /// <param name="info"></param> private void Print(string info) { if (this.InvokeRequired) { this.Invoke((Action)delegate() { DateTime now = DateTime.Now; string time_str = now.ToString("hh:mm:ss"); textBox1.AppendText(time_str + " " + info + "\r\n"); }); } else { DateTime now = DateTime.Now; string time_str = now.ToString("hh:mm:ss"); textBox1.AppendText(time_str + " " + info + "\r\n"); } } }