Python——Socket编程
参考博文:https://www.cnblogs.com/linhaifeng/articles/6129246.html
网络基础参考博文:https://www.cnblogs.com/linhaifeng/articles/5937962.html
一、什么是Scoket?
二、套接字
1、套接字发展史
题外话:进程跟进程之间是无法通信的;套接字的发展就是解决此问题。
2、两种类型的套接字
AF是Address Family 的缩写,意为地址家族。
此次只关心 AF_INET 网络编程使用。
4、TCP套接字工作流程
基于上面的流程,来举个简单的通信例子:
先来看下服务端的代码:
接着是客户端的代码:
5、三次握手与四次挥手
简略图解:
注意:主动断开链接的不一定是客户端
可以通过实验,链接百度,会发觉主动断开的是服务端。因为在大并发的情况下,服务端不会保留你客户端的链接,多保留一分钟会多占用一分钟资源(为了节省资源)。
为什么握手是三次,挥手是四次?
因为握手是建立消息通道;而挥手是断开消息通道,谁数据发完了谁发起断开的请求,回应了发来的消息后,还要再发送一次断开链接的请求
如果还不懂,百度其他文章,理解三次握手与四次挥手。
6、半链接池
半链接意为:一整个链接没有建立成功都是半链接。
backlog:半链接池
服务端可能同时接受到很多客户端的涌进,太多则响应不过来。所以把客户端的接入都放在backlog里,服务端从backlog里取出链接来进行回应。超出backlog的容量都要排队。
对应到程序当中的位置就是:
7、TCP客户端服务端循环发消息
服务端代码:
客户端代码:
8、socket收发消息原理
上面代码中,如果发个空 “ ”(回车,不是空格),程序则会卡主。为什么会卡主呢?先看下面:
内存分为用户态和内核态。应用程序属于用户态内存。操作系统属于内核态内存。发消息会从用户态内存发到内核态内存,再由操作系统接收,操作系统再操作网卡发送。另一边网卡接收后又交给操作系统,操作系统再给内核态内存,后面步骤依旧。recv(1024)是工作在socket应用程序里;1024是从内核态内存里取出的。send()发送也是发到内核态内内存。
之所以“空”没有反应,是因为客户端发送“空”到内核态内存,内核态内存则会什么都没接收到,既然没有接收到,也不会发送到服务端。所以此时卡主是因为客户端自己的内核态缓存里没东西,服务端的内核态内存什么都没有接收到,所以卡主。
解决“空”问题:
加这一句话就能解决
9、TCP服务端循环链接请求收发消息
服务端与客户端不可能通话一次就挂断,应该是服务端一直挂着,来了客户端就通话,客户端挂断,服务端就继续等下一个客户端。所以在等待客户端连接前加一个循环就能解决:
服务端客户端都在正常运行,如果客户端突然非正常关闭,服务端会抛出异常:
解决这种问题,用一个异常处理就可以解决:
10、基于TCP实现远程命令(类似于xshell)
小练习:客户端发送命令,服务端接收命令并执行,把执行后的结果返回客户端
先来学习一个模块:
subprocess.Popen('dir',shell = True) # 用shell工具解释前面的命令
再学习一个概念:两个程序间是无法通信的,需要通过一个介质来进行通信,这个介质就是管道,我们可以控制这个管道。
stdout #标准输出
stdin #标准输入
stderr #标准错误输出,有错误时不会在stdout里,在stderr里
举个例子:
当你输出什么东西显示在屏幕上时(就像是Linux的命令行,你打什么东西就会立即显示出来),也属于两个程序间的通信,屏幕是一个程序。这时可以控制管道,先把内容扔进管道里,需要的时候再把东西从管道里拿出来。
所以
subprocess.Popen('dir',shell = True,stdout = subprocess.PIPE) # PIPE是管子的意思。代表把输出结果交给管道
所以类似的效果是:
所以最终服务端代码:
客户端代码:
11、总结TCP套接字
TCP服务端流程:
TCP客户端流程:
12、注意
有时把程序关了,端口也不会立马释放。
方法一:
方法二:
此方法不细讲,有兴趣的自己去搜。
三、基于UDP的套接字
1、UDP套接字流程:
服务端:
客户端:
可以发现,代码部分少了listen(),listen是用来挂起链接请求,等待链接的建立。UDP根本没有链接一说,所以就没有listen。
实例:
服务端:
客户端:
UDP的接收是recvfrom,收的是一个元组的形式
2、recv与recvfrom的区别
在讲区别之前,先讲一下,通过实验可以发现,UDP的服务端可以对多个客户端通信;而TCP不行,服务端需要与这个客户端断开链接后,才能与下一个客户端通信。
为什么UDP可以?因为UDP不用建立链接
可以实验发现,UDP发送“空”,能发的出去,服务端也能接收,其原因是:
四、粘包现象
1、粘包现象
注意:这里的代码用的是 基于TCP实现远程命令 里的代码
当我们输入 dir 时,可看到
显示的是当前路径下的文件
输入ipconfig 时,查看ip相关信息:
这时,如果再输入 dir ,查看当前路径文件的信息时,就会发生奇怪的事:
可以看到,这里明明输入 dir ,显示的还是 ipconfig 的信息,这种现象就是粘包现象
上一次还没收完的信息,这一次继续接收。原因在于缓冲区不够大。就是下面这个设置:
只有TCP有粘包现象,UDP没有。 UDP中,如果上一次信息没有接收完,就会直接扔掉。
不同平台显示的结果不一样,下面是UDP在Windows中的异常:
题外话:UDP哪怕只有客户端,也可以执行程序,当然最终结果是卡主。因为能发出去,但是没有接收信息,最终就是卡主。所以说UDP是不可靠传输,只管发,不管收。
2、什么是粘包
五、socketserver实现并发
1、socketserver模块实现并发效果
通过导入socketserver模块实现并发:
服务端代码:
客户端代码:
两个客户端发送,服务端都能接收
2、socketserver模块
两大类:
server 类:处理链接
request 类:处理通信
进到模块内部查看代码,可以发现里面有很多的类,其中也有互相继承,看下图:
它们的相互继承关系:
3、socketserver源码分析tcp版本
自己看源码去理解!
4、socketserver源码分析udp版本
自己看源码去理解!