day8_Socket问题和Socketserver

动态导入

方法1:import方式


#!/usr/bin/env python
# -*- coding:utf-8 -*-

mod = __import__('lib.aa')#__import__只到lib
print(mod)
c_instance = getattr(mod.aa,'C')
c_obj = c_instance()
print(c_obj.name)
#输出
<module 'lib' from '/root/py/学习/day8/动态导入/lib/__init__.py'>
liyanan

方法2:importlib方式

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import importlib

mod = importlib.import_module('lib.aa')#到达aa这个目标
print(mod)
c_instance = mod.C
c_obj = c_instance()
print(c_obj.name)
#输出
<module 'lib.aa' from '/root/py/学习/day8/动态导入/lib/aa.py'>
liyanan

小结:
1、import方式是python内置函数,是python自己用的,而且知道第一层,所以极力不推荐
2、importlib这种方式是官方推荐的,用起来比较简单,而且导入到那哪里就到哪里,极力推荐


断言

python中断言,就是做一些程序的检查工作,就是在执行之前需要做的一些检查,比如类似于安检一样,合格的就能过,不合格的就不能过。也类似于银行转账工作,真正的转账之前是不是要检查用户各方面,是不是账号密码是不是都没有问题,转的钱必须没有问题,所以你在这个操作之前做一下检查。
作用:断言被用作你接下来的程序执行,如果后面程序依赖于前面的程序,后面的程序有很重要,就是后面的程序执行肯定不能出错,所以在执行之前要做检查工作。
断言的使用:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

class C(object):
    def __init__(self):
        self.name = 'liyanan'

c_obj = C()

assert c_obj.name =='liyanan'#断言的格式

print('断言没出错')
#输出
断言没出错

断言出错:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

class C(object):
    def __init__(self):
        self.name = 'liyanan'

c_obj = C()

assert c_obj.name =='li'#断言的格式

print('断言有没有出错呢?')
#输出
Traceback (most recent call last):
  File "/root/py/学习/day8/断言/断言.py", line 10, in <module>
    assert c_obj.name =='li'#断言的格式
AssertionError

断言错误,没有运行到下一步的‘print’,用于重要程序的阻断和判断。


socket问题

ssh的例子在上一篇day7中讲到了,但是有一个问题,我们再演示一遍,结果如下:
这里写图片描述
输入pwd没有显示出linux当前的目录,而是附带了上个命令的结果,原因是大小只设置了缓冲区1024。服务端第一次全部发送给客户端,客户端只接收自己部分的字节数,剩下的字节数被存储在 IO缓冲区 中,下一次你再用send的时候,你以为接收的是新的数据,其实不是,它会先把缓冲区里的数据发出去,然后你的新数据再放到缓冲区中,直到缓冲区满了再发出去。这就导致结果会一直推后,设置成更大的缓存区也不是个很好的办法。
我们可以让客户端多收几次,但是,客户端需要收多少次,才能把这个命令返回的结果全部收回来呢?并且怎么确定这条命令返回的结果已经被全部收回来了呢?


cocket接收大数据

解决客户端收多少次,我们可以引入大小的概念,服务端给客户端发数据之前,先计算一下给客户端要发多少数据,我先判断 len 一下,就 ok 了,先让客户端知道服务端发送过来的大小,比如说发过来的是5k大小,客户端接收到了这个5k大小以后,就知道需要接收多少次了,循环接收,直到5k数据全部接收完毕为止。
客户端:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket

client = socket.socket()
client.connect(("0.0.0.0", 6969))

while True:
    msg = input(">>>:").strip()
    if len(msg) == 0: continue
    client.send(msg.encode('utf-8'))
    msg_res_size = client.recv(1024)#接受命令的长度
    print('命令结果大小',msg_res_size.decode())
    recevied_size = 0 #接收的数据开始大小
    recevied_data = b''#客户端每次发来内容的计数器
    while recevied_size < int(msg_res_size.decode()): #当接收的数据大小 小于 客户端发来的数据
        msg_res = client.recv(1024)
        recevied_size+=len(msg_res)#每次收到的服务端的数据有可能小于1024,所以必须用len判断
        print(recevied_size)
        recevied_data+=msg_res
    else:
        print(recevied_data.decode('utf-8','ignore'))
        print('接收大小',recevied_size)

client.close()

服务端:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket,time
import os # 导入os模块

sever = socket.socket()
sever.bind(("0.0.0.0", 6969))
sever.listen()
while True:
    conn, addr = sever.accept()
    print("电话来了",addr)
    while True:
        data = conn.recv(1024)
        if not data: break
        print('执行指令',data)

        msg_res = os.popen((data.decode())).read()  # 调用linux命令
        print('发送之前',len(msg_res))
        if len(msg_res) ==0:
            msg_res = '输入错误'
        conn.send(str(len(msg_res.encode())).encode())#发送大小给客户端
        conn.send(msg_res.encode())  # 执行的命令返回值
        print('send done')
sever.close()

输出

>>>:ifconfig
ifconfig
命令结果大小 1280ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.224.128  netmask 255.255.255.0  broadcast 192.168.224.255
        inet6 fe80::c9a6:298e:c35b:36c5  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:3f:08:7e  txqueuelen 1000  (Ethernet)
        RX packets 3015  bytes 307695 (300.4 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1954  bytes 647060 (631.8 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1  (Local Loopback)
        RX packets 194  bytes 17900 (17.4 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 194  bytes 17900 (17.4 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

virbr0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 192.168.122.1  netmask 255.255.255.0  broadcast 192.168.122.2
Traceback (most recent call last):
  File "/root/py/学习/day8/黏包问题/client.py", line 16, in <module>
    while recevied_size < int(msg_res_size.decode()): #当接收的数据大小 小于 客户端发来的数据
ValueError: invalid literal for int() with base 10: '1280ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500\n        inet 192.168.224.128  netmask 255.255.255.0  broadcast 192.168.224.255\n        inet6 fe80::c9a6:298e:c35b:36c5  prefixlen 64 

Process finished with exit code 1

出错了,出错的原因是是服务端两次发送给客户端的数据粘在一起了。
这里写图片描述


黏包问题的解决

1、sleep,
这个样子就可以使缓冲区超时,就不在等下一次的了,这样就可以和下一条命令隔离开了
服务器端:

import socket,time
import os # 导入os模块

sever = socket.socket()
sever.bind(("0.0.0.0", 6969))
sever.listen()
while True:
    conn, addr = sever.accept()
    print("电话来了",addr)
    while True:
        data = conn.recv(1024)
        if not data: break
        print('执行指令',data)

        msg_res = os.popen((data.decode())).read()  # 调用linux命令
        print('发送之前',len(msg_res))
        if len(msg_res) ==0:
            msg_res = '输入错误'
        conn.send(str(len(msg_res.encode())).encode())#发送大小给客户端
        time.sleep(0.5)
        conn.send(msg_res.encode())  # 执行的命令返回值
        print('send done')
sever.close()

输出:

>>>:ls
ls
命令结果大小 32
32
client.py
__init__.py
server.py

接收大小 32
>>>:ifconfig
ifconfig
命令结果大小 1280
1024
1280
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.224.128  netmask 255.255.255.0  broadcast 192.168.224.255
        inet6 fe80::c9a6:298e:c35b:36c5  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:3f:08:7e  txqueuelen 1000  (Ethernet)
        RX packets 3192  bytes 324404 (316.8 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2062  bytes 668844 (653.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1  (Local Loopback)
        RX packets 233  bytes 23213 (22.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 233  bytes 23213 (22.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

virbr0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 192.168.122.1  netmask 255.255.255.0  broadcast 192.168.122.255
        ether 52:54:00:83:37:c9  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


接收大小 1280
>>>:

这个方法虽然可以解决黏包问题,但是需要等待0.5s最少。在一些场景中不能使用。我们可以通过下面的方法。
2、在服务端来一个等待客户端确认,就ok了,这个确认不需要我们用户输入,而是客户端自动的给你来这个响应,就是说,客户端自动的写好代码,自动的给服务器一个响应,只要收到服务端的数据大小,我就立刻给服务器一个响应,就是在第一次send和第二次send之前插入一个交互,就能把数据分开了。
客户端代码改进:

import socket

client = socket.socket()
client.connect(("0.0.0.0", 6969))

while True:
    msg = input(">>>:").strip()
    if len(msg) == 0: continue
    client.send(msg.encode('utf-8'))
    msg_res_size = client.recv(1024)#接受命令的长度
    print('命令结果大小',msg_res_size.decode())
    client.send('我准备好了接收了,可以发射'.encode())#改进黏包
    recevied_size = 0 #接收的数据开始大小
    recevied_data = b''#客户端每次发来内容的计数器
    while recevied_size < int(msg_res_size.decode()): #当接收的数据大小 小于 客户端发来的数据
        msg_res = client.recv(1024)
        recevied_size+=len(msg_res)#每次收到的服务端的数据有可能小于1024,所以必须用len判断
        print(recevied_size)
        recevied_data+=msg_res
    else:
        print(recevied_data.decode('utf-8','ignore'))
        print('接收大小',recevied_size)

client.close()

服务端改进:

import socket,time
import os # 导入os模块

sever = socket.socket()
sever.bind(("0.0.0.0", 6969))
sever.listen()
while True:
    conn, addr = sever.accept()
    print("电话来了",addr)
    while True:
        data = conn.recv(1024)
        if not data: break
        print('执行指令',data)

        msg_res = os.popen((data.decode())).read()  # 调用linux命令
        print('发送之前',len(msg_res))
        if len(msg_res) ==0:
            msg_res = '输入错误'
        conn.send(str(len(msg_res.encode())).encode())#发送大小给客户端
        #time.sleep(0.5)
        clent_ack = conn.recv(1024)#改进黏包,等待客户端确认
        conn.send(msg_res.encode())  # 执行的命令返回值
        print('send done')
sever.close()

文件的发送

用socket实现文件下载,思路如下:
1、读取文件名
2、检测文件是否存在
3、打开文件
4、检测文件大小
5、发送文件大小给客户端
6、等客户确认
7、开始边读边发数据
8、发送md5值给客户端校验

功能 实现:
服务端:(获取命令和文件名->判断文件是否存在->打开文件->获取文件大小->发送文件大小给客户端->等待客户端确认->边读边发)

import socket,time
import os # 导入os模块

sever = socket.socket()
sever.bind(("0.0.0.0", 6969))
sever.listen()
while True:
    conn, addr = sever.accept()
    print("电话来了",addr)
    while True:
        data = conn.recv(1024)
        if not data: break
        print('执行指令',data)
        msg,filename = data.decode().split()#接受客户端发过来的命令和文件名
        print(filename)
        if os.path.isfile(filename):#文件存在
            with open(filename,'rb') as f:
                file_size = os.stat(filename).st_size #获取文件大小
                conn.send(str(file_size).encode())#发送文件大小
                conn.recv(1024)
                for line in f:
                    conn.send(line) #边读边发送给客户端

        print('send done')
sever.close()

客户端(判断是否是下载命令(get) ->发送下载命令和文件名 ->获取文件大小->发送确认信息->判断时候已经全部接收)

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket

client = socket.socket()
client.connect(("0.0.0.0", 6969))

while True:
    msg = input(">>>:").strip()
    if len(msg) == 0: continue
    if msg.startswith('get'):
        client.send(msg.encode())#发送下载的命令和文件名
        server_respose = client.recv(1024)#接收文件大小
        print('server response:',server_respose)
        client.send('ready to recv file'.encode())#发送确认信息
        file_total_size = int(server_respose.decode())
        revived_size = 0#初始化大小
        filename = msg.split()[1]#获取文件名
        with open(filename+'.cp','wb')as f:
            while revived_size<file_total_size:#接收文件的大小小于总文件大小
                data = client.recv(1024)
                revived_size+=len(data)#接收大小
                f.write(data)
            else:
                print(file_total_size,revived_size)
                print('文件接收完了')


client.close()

然后加上md5的验证
服务端:(生成md5的对象->计算MD5值->生成16进制的形式->编码后发送给客户端)

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket,time,hashlib
import os # 导入os模块

sever = socket.socket()
sever.bind(("0.0.0.0", 6969))
sever.listen()
while True:
    conn, addr = sever.accept()
    print("电话来了",addr)
    while True:
        data = conn.recv(1024)
        if not data: break
        print('执行指令',data)
        msg,filename = data.decode().split()#接受客户端发过来的命令和文件名
        print(filename)
        if os.path.isfile(filename):#文件存在
            m = hashlib.md5()#(md5改动)生成md5的对象
            with open(filename,'rb') as f:
                file_size = os.stat(filename).st_size #获取文件大小
                conn.send(str(file_size).encode())#发送文件大小
                conn.recv(1024)#等待ack
                for line in f:
                    m.update(line)#(md5改动)计算md5值
                    conn.send(line) #边读边发送给客户端
                print('file md5',m.hexdigest())
            conn.recv(1024)#(md5改动)等待客户确认发送md5值

        print('send done')
sever.close()

客户端:(生成MD5值->计算接收的数据的MD5值->生成接收数据的MD5值16进制的形式->发送接收MD5值确认信息->接收客户端的MD5值->客户端和服务端的MD5值做比较)

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket,hashlib

client = socket.socket()
client.connect(("0.0.0.0", 6969))

while True:
    msg = input(">>>:").strip()
    if len(msg) == 0: continue
    if msg.startswith('get'):
        client.send(msg.encode())#发送下载的命令和文件名
        server_respose = client.recv(1024)#接收文件大小
        print('server response:',server_respose)
        client.send('ready to recv file'.encode())#发送确认信息
        file_total_size = int(server_respose.decode())
        revived_size = 0#初始化大小
        filename = msg.split()[1]#获取文件名
        m = hashlib.md5()#(md5改动)生成md5对象
        with open(filename+'.cp','wb')as f:
            while revived_size<file_total_size:#接收文件的大小小于总文件大小
                data = client.recv(1024)
                revived_size+=len(data)#接收大小
                m.update(data)#(md5改动)计算数据接收的md5值
                f.write(data)
            else:
                print(file_total_size,revived_size)
                client_md5_vault = m.hexdigest()#(md5改动)生成接收数据的md5值16进制形式
                client.send('ready to recv file md5 value'.encode())
                server_md5_vault = client.recv(1024)#(md5改动)接收服务端发送的md5值
                if client_md5_vault == server_md5_vault.decode():#(md5改动)两个md5进行比较
                    print('文件接收完了')
                else:
                    print(client_md5_vault,server_md5_vault.decode())


client.close()

小结:

1、上传和下载都是以客户端或者服务端加载文件,然后另外一边接收再写入文件。
2、解决粘包问题,在接收数据大小后需要等待确认信息。
3、看文件能都上传和下载,应该用MD5值去校验。
4、创建一个无限大的文件,请用:dd if=/dev/sda1 of=文件名,比如创建test.txt,则:dd if=/dev/sda1 of=test.txt
5、获取一个文件的大小:os.stat(文件名).st_size


改良md5的黏包问题

之前解决粘包的方法,是客户端发送一个请求,等待服务端的确认的这样的一个笨方法。下面我们用另外一种方法:就是客户端已经知道可接收多少数据了,既然客户端已经知道接收多少数据了,那么客户端在接收数据的时候,正好接收已经知道的数据,不就ok了吗?就是说我循环了正好是收到已经知道的那些数据。比如:我要发5M的数据,我正好收到5M的数据,然后就不往下再收了,因为它有可能跟MD5值黏在一块了,本来说是发5M文件,结果你发了5.1M,那么客户端正好收5M的话,那客户端的0.1M是不是就不收了,不收了之后我就把客户端把文件存下来,再来recive一下,下面recive的正好是0.1M。
代码实现:
服务端:(生成md5对象->发送的数据生成MD5值->发送MD5 值)

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket,time,hashlib
import os # 导入os模块

sever = socket.socket()
sever.bind(("0.0.0.0", 6969))
sever.listen()
while True:
    conn, addr = sever.accept()
    print("电话来了",addr)
    while True:
        data = conn.recv(1024)
        if not data: break
        print('执行指令',data)
        msg,filename = data.decode().split()#接受客户端发过来的命令和文件名
        print(filename)
        if os.path.isfile(filename):#文件存在
            m = hashlib.md5()#(md5改动)生成md5的对象
            with open(filename,'rb') as f:
                file_size = os.stat(filename).st_size #获取文件大小
                conn.send(str(file_size).encode())#发送文件大小
                conn.recv(1024)#等待ack
                for line in f:
                    m.update(line)#(md5改动)计算md5值
                    conn.send(line) #边读边发送给客户端
                print('file md5',m.hexdigest())
            #conn.recv(1024)#(md5改动)等待客户确认发送md5值
            conn.send(m.hexdigest().encode())#((md5改良))生成md5值并发送给客户端

        print('send done')
sever.close()

客户端:(文件大小-接收大小是否大于1024 ->获取size值->recive(size)->下面再次recive时就是MD5值了)

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket,time,hashlib
import os # 导入os模块

sever = socket.socket()
sever.bind(("0.0.0.0", 6969))
sever.listen()
while True:
    conn, addr = sever.accept()
    print("电话来了",addr)
    while True:
        data = conn.recv(1024)
        if not data: break
        print('执行指令',data)
        msg,filename = data.decode().split()#接受客户端发过来的命令和文件名
        print(filename)
        if os.path.isfile(filename):#文件存在
            m = hashlib.md5()#(md5改动)生成md5的对象
            with open(filename,'rb') as f:
                file_size = os.stat(filename).st_size #获取文件大小
                conn.send(str(file_size).encode())#发送文件大小
                conn.recv(1024)#等待ack
                for line in f:
                    m.update(line)#(md5改动)计算md5值
                    conn.send(line) #边读边发送给客户端
                print('file md5',m.hexdigest())
            #conn.recv(1024)#(md5改动)等待客户确认发送md5值
            conn.send(m.hexdigest().encode())#((md5改良))生成md5值并发送给客户端

        print('send done')
sever.close()

输出

ssh://root@192.168.224.128:22/usr/local/python/bin/python3 -u /root/py/学习/day8/mdclient.py
>>>:get a.txt
get a.txt
server response: b'1'
last receive: 1
1 1
server file md5: 0cc175b9c0f1b6a831c399e269772661
client file md5: 0cc175b9c0f1b6a831c399e269772661
>>>:

socketserver

我们之前的用户都是不支持多并发的,所以我们现在需要一个支持多用户,实现一个并发处理,我们接下来就不能不接触socketserver这个模块。前面讲的socket都是铺垫,后面需要真正使用的是socketserver。
socketserver类型
1、TCPserver
说明:这个是用于TCP协议的,它在客户端和服务器之间提供连续的数据流

class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)

2、UDPServer
说明:用于UDP协议的,并非连续的数据包,中间传输可能会丢失。传入的参数和TCPServer是一样的

class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)

3、UnixStreamServer和UnixDatagramServer
说明:这个很少用在TCP和UDP类中,但是用正在Unix领域里的sockets;传入的参数和TCPServer是一样的。

class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass,bind_and_activate=True)

4、五个类实例化过程
这里写图片描述


创建cocketserver步骤

1、必须自己创建一个请求处理类,并且这个类要继承BaseRequestHandlerclass,并且还有重写父类里的handle()
2、你必须实例化TCPServer,并且传递server ip和你在第一步创建的请求处理类给这个TCPServer
3、接下来调server.handle_request()(只处理一个请求)或者server.serve_forever()(处理多个客户端请求,永远执行)
4、调用server_close()去关闭socket
代码实现:
服务器端:(代码逻辑:先定义一个handler类->实例化TCPServer->serve_forever()实现处理多个客户端请求)

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socketserver

class MyTCPhandler(socketserver.BaseRequestHandler):#继承BaseRequestHandler这个基类
    def handle(self):
        while True:
            try:
                self.data = self.request.recv(1024)
                print('{0} write:'.format(self.client_address[0]))
                print(self.data)
                self.request.send(self.data.upper())
            except ConnectionResetError as e:#抓去异常,这个异常当客户端断开服务端出现断开,这个只有在python3.0才会有
                print('error:',e)
                break

if __name__ == '__main__':
    host,port = '0.0.0.0',9999
    server = socketserver.TCPServer((host,port),MyTCPhandler)#每起一个TCPServer都会实例化MyTCPHandler这个类
    server.serve_forever()#实现多个连接
    server.server_close()#关闭socketserver

注:客户端所有的交互都是在handler里面完成的,每一个请求过来之后,它的请求就会被分配,它的请求的处理规则就是咋子handle里面规定的。服务器收到客户端的数据为空,表明客户端已经断开。
客户端:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket

client = socket.socket()
client.connect(('0.0.0.0',9999))
while True:
    cmd = input('>>>:').strip()
    if len(cmd)==0:continue
    client.send(cmd.encode())
    cmd_res = client.recv(1024)
    print(cmd_res.decode('utf-8','ignore'))

client.close()

sockerserver实现多用户并发:
1、多线程
主要在实例化TCPServer时,采用ThreadingTCPServer这种多线程方式。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socketserver

class MyTCPhandler(socketserver.BaseRequestHandler):#继承BaseRequestHandler这个基类
    def handle(self):
        while True:
            try:
                self.data = self.request.recv(1024)
                print('{0} write:'.format(self.client_address[0]))
                print(self.data)
                self.request.send(self.data.upper())
            except ConnectionResetError as e:#抓去异常,这个异常当客户端断开服务端出现断开,这个只有在python3.0才会有
                print('error:',e)
                break

if __name__ == '__main__':
    host,port = '0.0.0.0',9998
    server = socketserver.ThreadingTCPServer((host,port),MyTCPhandler)#改变之处
    server.serve_forever()#实现多个连接
    server.server_close()#关闭socketserver

ThreadingTCPServer表示服务器每收到客户端一个请求,服务器就会开启一个线程,开启一个线程跟这个链接交互,这个新的线程就是独立的线程,如果你有10个线程,代表可以干10件事。
2、多进程
主要在实例化TCPServer时,采用ForkingTCPServer这种多线程方式,但是这种方式在windows上不好使,需要在Linux上去执行,因为windows和linux处理多进程方式不一样。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socketserver

class MyTCPhandler(socketserver.BaseRequestHandler):#继承BaseRequestHandler这个基类
    def handle(self):
        while True:
            try:
                self.data = self.request.recv(1024)
                print('{0} write:'.format(self.client_address[0]))
                print(self.data)
                self.request.send(self.data.upper())
            except ConnectionResetError as e:#抓去异常,这个异常当客户端断开服务端出现断开,这个只有在python3.0才会有
                print('error:',e)
                break

if __name__ == '__main__':
    host,port = '0.0.0.0',9998
    server = socketserver.ForkingTCPServer((host,port),MyTCPhandler)#采用ForkingTCPServer实现多进程
    server.serve_forever()#实现多个连接
    server.server_close()#关闭socketserver

这样把socketserver.TCPServer换成socketserver.ThreadingTCPServer,客户端每连进一个来,服务器端就会分配一个新的线程来处理这个客户端的请求。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值