续上篇《你也能够写个聊天程序 - C# Socket学习1》css
这里说的服务器是Web服务器,是相似IIS、Tomcat之类的,用来响应浏览器请求的服务。html
首先浏览器的请求是HTTP协议。咱们上一篇说过,HTTP是短链接,用完就断开,是无状态的。因此咱们在等待响应的时候不须要另外开个线程循环等待。
也就是咱们只须要经过Socket和服务器创建链接,而后发送请求,而后接收服务器的响应,这样就完成了一次请求。
但是,咱们通常访问网页的时候都是经过域名,没有IP也没有端口,怎么和服务器创建链接了。这里就须要用到咱们上篇介绍的几个类了:git
//根据DNS获取域名绑定的IP foreach (var address in Dns.GetHostEntry("www.baidu.com").AddressList) { Console.WriteLine($"百度IP:{address}"); } //字符串转IP地址 IPAddress ipAddress = IPAddress.Parse("192.168.1.101"); //经过IP和端口构造IPEndPoint对象,用于远程链接 //经过IP能够肯定一台电脑,经过端口能够肯定电脑上的一个程序 IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 80);
对于HTTP没有显示端口默认都是80 (为了简单这里就先不考虑HTTPS了)
知道了IP和端口,链接是能够创建了,为了获得正确的响应,咱们应该给服务器发送什么消息呢?这里就须要用到HTTP协议了。
具体协议这里就不说了,咱们先F12看看浏览器的请求报文,而后依葫芦画瓢试试,以http://fanyi-pro.baidu.com为例。(如今找个非HTTPS的地址也是不容易了)
而后咱们代码实现以下:github
void ...() { //获得主机信息 IPHostEntry ipInfo = Dns.GetHostEntry(new Uri("http://fanyi-pro.baidu.com").Host); //取得IPAddress[] IPAddress[] ipAddr = ipInfo.AddressList; //获得服务器ip IPAddress ip = ipAddr[0]; //组合远程终结点 IPEndPoint ipEndPoint = new IPEndPoint(ip, 80); //建立Socket 实例 Socket socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //尝试链接 socketClient.Connect(ipEndPoint); //发送请求 Send(socketClient); //接收服务器的响应 Receive(socketClient); } //接收来自服务端的消息 void Receive(Socket socketClient) { byte[] data = new byte[1024 * 1024]; while (true) { //读取客户端发送过来的数据 int readLeng = socketClient.Receive(data, 0, data.Length, SocketFlags.None); textBox2.AppendText($"{socketClient.RemoteEndPoint}:{Encoding.UTF8.GetString(data, 0, readLeng)}\r\n"); } } //发送消息到服务端 void Send(Socket socketClient) { //为了方便演示,仅用请求报文的前两行便可。(切记:须要严格按照报文格式。如,最后须要连续两次换行) var msg = $"GET / HTTP/1.1\r\nHost: {new Uri(textBox1.Text).Host}\r\n\r\n"; socketClient.Send(Encoding.UTF8.GetBytes(msg)); }
整个流程也就是:web
【注意】:发送报文的时候须要严格按照报文格式。如,最后须要连续两次换行、行末不能有空格等。编程
效果图:
数组
Web服务器的实现和咱们上一篇的Socket聊天服务端其实也差很少。
不一样之处就在于,解析请求报文,而后按HTTP协议回复标准的响应报文(我这里为了简单,就没有按标准的协议来玩,仅仅只是实现了表面的效果)
代码以下:浏览器
void ...() { //1 建立Socket对象 Socket socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //2 绑定ip和端口 IPAddress ip = IPAddress.Parse("127.0.0.1"); IPEndPoint ipEndPoint = new IPEndPoint(ip, 80); socketServer.Bind(ipEndPoint); //三、开启侦听(等待客户机发出的链接),并设置最大客户端链接数为10 socketServer.Listen(10); //阻塞等待客户端链接 Task.Run(() => { Accept(socketServer); }); } //4 阻塞等待客户端链接 private static void Accept(Socket socketServer) { while (true) { //阻塞等待客户端链接 Socket newSocket = socketServer.Accept(); Task.Run(() => { Receive(newSocket); }); } } //5 读取客户端发送过来的报文 private static void Receive(Socket newSocket) { byte[] data = new byte[1024 * 1024]; while (newSocket.Connected) { //读取客户端发送过来的数据 int readLeng = newSocket.Receive(data, 0, data.Length, SocketFlags.None); //读取客户端发来的请求报文 var requst = Encoding.UTF8.GetString(data, 0, readLeng); //解析请求报文的请求路径(能够解析请求路径、请求文件、文件类型) var requstFile = requst.Split("\r\n")[0].Split(" ")[1]; //回复客户端响应报文 Send(newSocket, requstFile); } } //6 回复客户端响应报文 private static void Send(Socket newSocket, string requstFile) { //这里若是请求的根目录,默认显示Index.html if (requstFile == "/" ) requstFile = "/Index.html"; var msg = File.ReadAllText(Directory.GetCurrentDirectory() + requstFile); //把消息内容转成字节数组后发送 newSocket.Send(Encoding.UTF8.GetBytes(msg)); //回复响应后立刻关闭链接 newSocket.Shutdown(SocketShutdown.Both); newSocket.Close(); }
效果以下:
由此咱们知道了.net core为何能够在不须要iis的状况下,一个黑窗体就提供了对网址的访问。其实也就是KestrelServer经过Socket绑定并监听端口提供的服务。
【注意】:咱们绑定的ip是127.0.0.1socketServer.Bind(ipEndPoint)
,因此咱们测试的时候只能在浏览器输入127.0.0.1或者localhost。若是想经过内外ip访问,咱们能够绑定任意ipIPAddress.Any
。如socketServer.Bind(new IPEndPoint(IPAddress.Any, port))
。服务器
对于HTTP/TCP可能你们多少都听过三次握手,但是在咱们在用Socket编写Web服务器的时候并无看到相关的东西啊,这是怎么回事。
由于咱们在客户端执行链接socketClient.Connect(ipEndPoint)
的时候已经进行了三次握手
具体可细读小坦克大佬的文章。
也就是说咱们在用C#的Socket、TCP、HttpClient的时候根本就不用关注这些细节。
另外套接字有三种不一样的类型:流套接字、数据报套接字和原始套接字。前二者是标准套接字,分别对应TCP和UDP。而原始套接字则更加底层更加牛逼,普通开发人员通常接触不到。
咱们说的HTTP、TCP、UDP之类都是网络协议,那协议究竟是什么?通俗的说其实只是你我他之间的一个约定而已,你们都按规定了来那就能够说是协议。
而HTTP又是创建在TCP之上的,也就是说基础协议以后再加约定又能够成为一种新的协议。下章咱们将用Socket来实现ModbusTCP协议对寄存器读和写。网络