你也可以写个服务器 - 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协议对寄存器读和写。
结束
- 本文已同步至索引目录:《物联网基础组件IoTClient开发系列》
- 完整demo:https://github.com/zhaopeiym/BlogDemoCode/tree/master/Socket编程/2HTTP
- 推荐阅读:https://www.cnblogs.com/TankXiao/archive/2012/10/10/2711777.html
你也可以写个服务器 - C# Socket学习2的更多相关文章
- 你也可以写聊天程序 - C# Socket学习1
简述 我们做软件工作的虽然每天都离不开网络,可网络协议细节却不是每个人都会接触和深入了解.我今天就来和大家一起学习下Socket,并写一个简单的聊天程序. 一些基础类 首先我们每天打开浏览器访问网页信 ...
- 你也可以写个聊天程序 - C# Socket学习1
原文:你也可以写个聊天程序 - C# Socket学习1 简述 我们做软件工作的虽然每天都离不开网络,可网络协议细节却不是每个人都会接触和深入了解.我今天就来和大家一起学习下Socket,并写一个简单 ...
- Socket学习总结系列(二) -- CocoaAsyncSocket
这是系列的第二篇 这是这个系列文章的第二篇,要是没有看第一篇的还是建议看看第一篇,以为这个是接着第一篇梳理的 先大概的总结一下在上篇的文章中说的些内容: 1. 整理了一下做IM我们有那些途径,以及我们 ...
- Socket 学习(三).4 UDP 穿透 客户端与客户端连接
效果图: 使用方法: 先 修改WinClient\bin\Debug 下面的 ip.ini,写上 服务器 IP地址. 客户端 与 客户端 通讯 之前 ,点击发送打洞消息 按钮,然后过一会再发送消息 ...
- 网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接
本文原作者:“水晶虾饺”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.引言 好多小白初次接触即时通讯(比如:IM或者消息推送应用)时,总是不 ...
- JavaSE 手写 Web 服务器(二)
原文地址:JavaSE 手写 Web 服务器(二) 博客地址:http://www.extlight.com 一.背景 在上一篇文章 <JavaSE 手写 Web 服务器(一)> 中介绍了 ...
- Java Socket 学习笔记
TCP协议的Socket编程 Socket:英文中的意思是插座.两个Java应用程序可以通过一个双向的网络通信连接实现数据交换,这个双向链路的一端称为一个Socket.Java中所有关于网络编程的类都 ...
- C++ Socket学习记录 -2
WinSock TCP 编程流程 TCP通信,就像是固定电话,首先是要安装基站,然后是将电话号绑定到电话,然后拨号,接通之后说事,完事之后还要挂电话(甭管谁先挂). 1.初始化环境 使用函数 int ...
- JavaSE 手写 Web 服务器(一)
原文地址:JavaSE 手写 Web 服务器(一) 博客地址:http://www.extlight.com 一.背景 某日,在 Java 技术群中看到网友讨论 tomcat 容器相关内容,然后想到自 ...
随机推荐
- 聊聊Spring Cloud Config
Spring Cloud Config 转自:https://blog.csdn.net/fjnpysh/article/details/71307311 现今这个时候,微服务大行其道,互联网应用遍地 ...
- [大数据学习研究]2.利用VirtualBox模拟Linux集群
1. 在主机Macbook上设置HOST 前文书已经把虚拟机的静态IP地址设置好,以后可以通过ip地址登录了.不过为了方便,还是设置一下,首先在Mac下修改hosts文件,这样在ssh时就不用输入ip ...
- 爬取凤凰网站财经类的新闻,函数时编程,可全部实现,由于内容量大,需要时间太长,服务器会禁止,为了防止,可以将time.sleep()设置的时间长点
import requests from selenium import webdriver import time def grasp(urlT): driver = webdriver.Chrom ...
- gallery的简单使用方法
Gallery意思是"画廊",就是一个水平可左右拖动的列表,里面可以放置多个图片,经常和ImageSwitcher一起使用 在使用ImageSwitcher时,需要传入一个View ...
- openresty域名动态解析
工作中使用openresty,使用第三方服务API通过域名访问.但是,域名通过DNS解析出来之后,在openresty是有 配置解析阶段 很多时候我们会在 Nginx 配置文件里配置上一些域名,比如配 ...
- abp(net core)+easyui+efcore实现仓储管理系统——EasyUI之货物管理一 (十九)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- [Optimized Python] 17 - Performance bottle-neck
前言 对于一门编程语言,没接触到“优化”和“库代码”的源码理解程度,不足以谈“掌握”二字. 本篇的学习笔记,同时也意味着自己终于触及到了Python的junior国际水准.(joke) 要学的东西有很 ...
- 初识Matplotlib-01
初识数据分析 大数据是一个含义广泛的术语,是指数据集,如此庞大而复杂的,他们需要专门设计的硬件和软件工具进行处理.该数据集通常是万亿或EB的大小.这些数据集收集自各种各样的来源:传感器,气候信息,公开 ...
- SpringBoot起飞系列-入门(一)
一.SpringBoot简介 1.1 什么是SpringBoot 说到spring系列,可能大家都很熟悉,spring.springmvc,美之名曰:spring全家桶,那么springboot其实也 ...
- 一步步构建.NET Core Web应用程序---仓储层,业务层的实现
前言 上一篇文章介绍了整个项目的结构,接下来向大家介绍一下 我的 仓储及业务层具体的实现思路,如果有更好的实现方式,希望大家及时指出!!! 构建过程 一,数据访问 首先在 DataProvider 中 ...