前言
上一次我们通过简单的测试完成了,从客户端发送信息给服务端,上次文章点这里,最后的效果如下
但是当我们想要连续发送数据时,这就显然不够用了,于是进阶版就来了。
进程、线程和协程
进程:
系统进行资源分配和调度的基本单位,是操作系统结构的基础,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
比如一台电脑上面的某个软件就是一个进程,而软件里面的一些功能就是线程。
线程:
有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。
协程:
协程是比线程更小的一种执行单元,可以认为是轻量级的线程,之所以说轻,其中一方面的原因是协程所持有的栈比线程要小很多,因为协程切换不需要花费时间,所以应用于ngix,大型的秒杀系统等。
总结
一个正常的连接大概分为以下三部分:
- 建立数据
- 接收数据
- 返回数据
所以协程可以解决以下的问题:
(1) 解决线程对的数据的时间:让接收数据的形式,变成非阻塞
(2) 解决线程切换的时间:不同线程,用协程解决线程切换的时间
总结:协程可以解决线程之间的切换问题
服务端
我们在原来的基础上进行修改,主要表现在使用了threading库函数,可以使用pip install threading进行安装。
import socket
import threading
#创建一个tcp socket ip 地址版本为Ip4
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.setblocking(False) # 设置套接字非阻塞,默认是阻塞模式 找epoll使用,解决网络等待的问题
#绑定IP和端口号
sock.bind(("127.0.0.1",8080))
#最大限度接收数
sock.listen(128)
def handle_func(new_sock):
while 1:
try:
data = new_sock.recv(4) # 接收数据
if len(data) == 0 : #如果接收的数据为空,则关闭通信
new_sock.close()
# sock.close()
else:
print(data)
except:
pass
while 1:
try:
#取出新的socket addr是IP,而端口是随机生成的底层做了
#accept()处理的连接都是三次握手的连接#
new_cli,addr = sock.accept()
print('来自一个新的连接', addr,type(addr))
task = threading.Thread(target=handle_func,args=(new_cli,))
task.start()
except:
pass
客户端
import socket
#创建一个tcp socket ip 地址版本为Ip4
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#设置为IP4
#指定服务器的ip和端口号
sock.connect(("127.0.0.1",8080))
while True:
data = input("请输入: ")
if data == 'Q' or data == 'q':
break
else:
sock.send(data.encode())
sock.close()
测试结果
客户端输入:
服务端输出:
当客户端重新运行进行通信时,会再次随机分配一个端口号进行连接后通信
小提示
运行时,依旧是先运行服务端,再运行客户端。
对于服务端代码用的两次try和except,是因为当服务端收到的信息为0时,会报出一个
[WinError 10035] 无法立即完成一个非阻止性套接字操作。
这样的错误,所以用try捕获一下就OK了。
最后,有不明白的地方都可以留言或者私信我!
创作不易,大侠请留步… 动起可爱的双手,来个赞再走呗 (๑◕ܫ←๑)