你也可以写个服务器 - C# Socket学习2

续上篇《你也可以写个聊天程序 - C# Socket学习1》

前言

这里说的服务器是Web服务器,是类似IIS、Tomcat之类的,用来响应浏览器请求的服务。

Socket模拟浏览器的Url Get请求

首先浏览器的请求是HTTP协议。我们上一篇说过,HTTP是短连接,用完就断开,是无状态的。所以我们在等待响应的时候不需要另外开个线程循环等待。
也就是我们只需要通过Socket和服务器建立连接,然后发送请求,然后接收服务器的响应,这样就完成了一次请求。
可是,我们一般访问网页的时候都是通过域名,没有IP也没有端口,怎么和服务器建立连接了。这里就需要用到我们上篇介绍的几个类了:

//根据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的地址也是不容易了)

然后我们代码实现如下:

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));
}

整个流程也就是:

  • 1、dns服务把域名解析成ip
  • 2、通过ip和端口和服务器建立连接(三次握手)
  • 3、获取html文档
  • 4、根据文档里面的链接(js、css、img)再重复以上过程

【注意】:发送报文的时候需要严格按照报文格式。如,最后需要连续两次换行、行末不能有空格等。

效果图:

用Socket实现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);

    //3、开启侦听(等待客户机发出的连接),并设置最大客户端连接数为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协议对寄存器读和写。

结束

原文地址:https://www.cnblogs.com/zhaopei/p/Socket2.html

时间: 2024-10-10 17:40:40

你也可以写个服务器 - C# Socket学习2的相关文章

C++ Socket 学习笔记

Socket学习笔记 以下均为整理,做参考之用. IP Address IP地址是指互联网协议地址(英语:Internet Protocol Address,又译为网际协议地址),是IP Address的缩写.IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异. IP地址被用来给Internet上的电脑一个编号.大家日常见到的情况是每台联网的PC上都需要有IP地址,才能正常通信.我们可以把"个人电脑"比作"

Socket 学习(三).4 DUP 穿透 客户端与客户端连接

效果图: 使用方法:  先 修改WinClient\bin\Debug  下面的 ip.ini,写上 服务器 IP地址. 客户端 与 客户端 通讯 之前 ,点击发送打洞消息 按钮,然后过一会再发送消息. 代码 很多, 参见: http://www.cnblogs.com/LeoWong/archive/2009/09/25/1574266.html 已经放在  MVC EF Bootstrap技术交流   206058845    里面的文件里面了.感兴趣的可以去下载.文件名 Socket穿透.

socket学习及各类错误码(部分转)

如果本地有多个网卡(即多个ip),要指定本地发送网卡,则在建立的socket上bind所指定的网卡进行connect和send操作.例子程序如下: #include <stdio.h>#include "WinSock2.h"#pragma comment(lib,"ws2_32.lib") SOCKET tcp_socket;SOCKADDR_IN tcpAddr;const int BufLen=1024;char SendBuf[BufLen];

Android socket 学习记录 之 执行new socket(ip, port)程序崩溃

这段时间在学习Android的socket编程,我不是专做APP的,做的是bootloader.驱动.hal.framework这个线的,也就是系统搭建和功能优化设计.为了打通这整条线,为此学习了不少东西,今天把Android的socket学习记录一下,以防止以后会出现这样的低级错误. 我这里是在极客学院的源码基础上做的自己的一些添加和修改,学习开始不就是先会修改么,举一反三,自然就很快学会了.由于看过视频和资料后就迫不及待的按照自己的想法想做一个功能,但是遇到麻烦了,就是执行new socke

《Linux高性能服务器编程》学习总结(九)——I/O复用

第九章      I/O复用 I/O复用技术是重要的提高服务器工作效率和性能的手段,Linux下实现I/O复用的系统调用主要有select.poll和epoll. 首先我们来看一下select的函数原型和常用的宏: 1 #include<sys/select.h> 2 int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout); 3 FD_ZERO(fd_

手写Tomcat服务器

预备知识 编写服务器用到的知识点 1) Socket 编程2) HTML3) HTTP 协议4) 反射5) XML 解析6) 服务器编写 Socket编程 https://www.cnblogs.com/bfcs/p/10790130.html HTML知识 HTML:HyperText Markup Language 超文本标记语言用于描述网页文档的一种标记语言 表单(form):与用户之间进行交互 method:请求方式 get/post get 数据量小,安全性低,默认方式 post 数据

腾讯高级工程师:如何从头开始写游戏服务器框架_转

转自: 腾讯高级工程师:如何从头开始写游戏服务器框架 本文作者:韩伟,腾讯互娱高级工程师,目前在 Next 产品中心研发创新类型游戏. 前言:从去年开始作者投入了一些具体游戏项目的开发,这些新的游戏项目,比较接近独立游戏的开发方式.在这个过程中,作者从头写了一个游戏服务器端的框架,以便获得更好的开发效率和灵活性.因此这篇文章便是该项目服务器框架的设计和实现过程的总结. PS:框架的基本运行环境是 Linux ,采用 C++ 编写.为了能在各种环境上运行和使用,采用了 gcc4.8 这个“古老”的

Android客户端与PC服务器通过socket进行交互实例(转)

一直以来对Android socket通信都很模糊,今天终于研究了一个网上的例子,自己又修改了下,算是对Android socket通信有点了解了. 下面是具体的代码,说明都在注释中了.需要注意的是,只有客户端发送了信息给服务器后,服务器才能响应客户端的输入,然后返回信息给客户端,这是客户端才能读取服务器返回的信息.如果客户端和服务器都处于等待对方的信息,那样就会造成阻塞,导致ANR了. 1.服务器端代码,就是一个java程序,与android无关,运行于服务器上,即PC机上. [java] v

android 发送UDP广播,搜寻服务器建立socket链接

应用场景:客户端(手机,pc)需要搜寻所在局域网内的服务器并获得服务器地址. 方法简介:客户端发送UDP广播,服务收到广播后得到客户端ip地址,然后向客户端发送一次socket链接,客户端收到socket链接,获得服务器地址. 相关知识: UPD.TCP.TCP是面向链接的,可靠的通信方式.UDP是面向非链接的通讯方式.TCP的建立比较麻烦,要经过"三次握手".而UDP的建立比较简单,发送方只管把内容发送出去,不管接收方是否收到.UDP的传输分为:单播,多播,广播.其中,多播和广播是通