10月24日用socketserver模块TCP和UDP的服务器

发布时间 2023-10-24 20:15:31作者: songjunwan

socketserver模块

为什么要考虑这个模块呢?因为真实情况下不一定只有一个客户端连接,如果我使用socket模块就无法实现一个服务器连接多个客户端同时回复客户端的数据,下面先展示一下这个情况图

这个结果图就说明了这个问题,这里可以牵扯一些线程socket拿来给单线程挺好的如果给它应用到多线程算了没戏,所以就需要用socketserver这个模块来实现,多线程可以暂时理解为多个线程就可以有多个进程同时使用

TCP协议的服务器以及客户端

服务器的代码

#使用socketserver模块

#导入模块
import socketserver


#自己定义一个了类,必须继承BaseRequestHandler,这是一个固定的继承能不改就不改
class Mytcp(socketserver.BaseRequestHandler):
    #这里必须重写handle方法
    def handle(self):
        try:
            while True: #通信循环
                # 给客户端回复消息
                # conn对象就是request
                # 接收数据
                data = self.request.recv(1024)
                print(data)

                #这里给linux的操作系统做准备的
                if len(data) ==0:
                    return
                # 发送数据
                self.request.send(data.upper())#这里把收到的数据大写发送回去

        except Exception:
            pass

if __name__ == '__main__':
    #实例化得到一个tcp连接的对象,Threading意思是,只要来了请求,就会自动开启线程来处理连接跟交互数据
    #第一个参数是绑定的地址(元组的形式),第二个参数传一个类(自己定义的类)
    duixiang = socketserver.ThreadingTCPServer(('172.17.8.49', 8080), Mytcp)#如果没有客户端连接就会在这里等待
    #监听
    duixiang.serve_forever()#这里的serve_forever是一直监听的意思

这里来细讲服务器的代码

第一步:首先导入socketserver这个模块

第二步:然后在定义一个类(其实主要修改hanle函数这个方法就行了)同时要继承牵扯固定格式(socketserver.BaseRequestHandler)能不改就不改,因为要修改的函数就是它里面的方法,不要修改!!!

第三步:给修改的函数里面添加功能,这里面的接收数据和发送数据多了self.request这个代码,它是一个约定俗成的,通常不要修改,要是修改就会很麻烦,所以记住这个格式就行了。它是用来接收客户端发来的数据和发送响应给客户端的数据的套接字对象。

第四步:使用全局调用的方式来运行(也可以不用,随自己的想法来运行),这里面的socketserver.ThreadingTCPServer当做固定格式就好了它代表这个TCP服务器然后它里面要传两个参数,第一参数要以元组的形式来把IP地址以及端口号绑定上去,然后第二个参数来传自己定义的类来使用类里面的方法

第五步:监听启动服务器这里的监听格式是serve_forever(),到这步后服务器就正式启动了。

客户端可以用以前TCP客户端的形式

import socket

soc = socket.socket()

soc.connect(('172.17.8.49', 8080))

while True:
  
    soc.send('yyy'.encode('utf-8'))
    print(soc.recv(1024))

结果图如下

这是拿来测试的代码尽量别用上面的方式来输出和收取,否则电脑死机概不负责

那么这个模块会不会也有粘包问题,这是必定的因为现在它是按TCP协议来写的服务器

UDP协议的服务器以及客户端

UDP协议的服务器和TCP协议的服务器代码相差无几,只用修改几处即可

服务器

#UDP服务器(pro版的socketserver版)
import socketserver


#自己定义一个了类,必须继承BaseRequestHandler,这是一个固定的继承能不改就不改
class Mytcp(socketserver.BaseRequestHandler):
    #这里必须重写handle方法
    def handle(self):
        print(self)

        #数据部分的处理
        #这时候request就是客户端发来的元组里面有数据有IP地址和端口号了
        #然后这是第一种方法赋值
        data, addr = self.request
        print(data.decode('utf-8'))#这是客户端发过来的数据同时给它进行解码的操作
        print(addr)#这是客户端的IP地址以及使用端口号的数据



        # #这是第二种方法索引
        # print(self.request[0])#这是客户端发过来的数据
        # print(self.request[1])#这是客户端

      


        #回复客户端的数据请求
        shuru = input('请输入回复的数据:')
        self.request[1].sendto(shuru.encode('utf-8'),self.client_address)





if __name__ == '__main__':
    
    #第一个参数是绑定的地址(元组的形式),第二个参数传一个类(自己定义的类)
    duixiang = socketserver.ThreadingUDPServer(('172.17.8.49', 8080), Mytcp)#如果没有客户端连接就会在这里等待
    # #监听
    duixiang.serve_forever()#这里的serve_forever是一直监听的意思

这个UDP服务器代码里面的监听必须要用

同时将socketserver.ThreadingUDPServer当做固定格式就好了它代表这个UDP服务器然后它里面要传两个参数,第一参数要以元组的形式来把IP地址以及端口号绑定上去,然后第二个参数来传自己定义的类来使用类里面的方法

这个UDP服务器是不需要使用recvfrom(1024)来收取客户端的数据,self.request它就是客户端发来的数据同时这里面还有客户端的IP地址以及端口号(IP地址和端口号是在一个元组里)

然后如何查看客户端发来的数据以及IP地址和端口号呢?

有两个方法
第一个是将数据、IP地址和端口号的所在的元组分别进行赋值到其它的变量里来查看和使用
第二个是利用索引的方法来查看和使用
这两个方法代码里面都有

然后客户端的代码如下

import socket

client = socket.socket(type=socket.SOCK_DGRAM)

while True:
    msg = input('输入请求:')
    client.sendto(msg.encode('utf-8'), ('172.17.8.49', 8080))

    data,ader = client.recvfrom(1024)

    print(data.decode('utf-8'))

这个代码没什么可讲的

修改UDP修改版

在和其它电脑测试中无法正确读取别人的IP地址和端口号,所以进行了修改:使用了self.client_address的方法来将IP地址和端口号它们两个的元组给拆开分别赋值,IP地址赋值给了client_IP,端口号赋值给了client_PORT

修改代码如下

#UDP服务器(pro版的socketserver版)
import socketserver


#自己定义一个了类,必须继承BaseRequestHandler,这是一个固定的继承能不改就不改
class Mytcp(socketserver.BaseRequestHandler):
    #这里必须重写handle方法
    def handle(self):

        #数据部分的处理
        #这时候request就是客户端发来的元组里面有数据有IP地址和端口号了
        #然后这是第一种方法赋值
        data, addr = self.request#addr数据不是客户端的
        print(data.decode('utf-8'))#这是客户端发过来的数据同时给它解码的操作
        

        #获取客户端的IP地址和端口号
        client_IP, client_PORT = self.client_address
        print(f'客户端的IP地址是{client_IP}它使用的端口号为{client_PORT}')


        # #这是第二种方法索引
        # print(self.request[0])#这是客户端发过来的数据
        # print(self.request[1])#这是客户端




        #回复客户端的数据请求
        shuru = input('请输入回复的数据:')
        self.request[1].sendto(shuru.encode('utf-8'),self.client_address)





if __name__ == '__main__':
    #实例化得到一个tcp连接的对象,Threading意思是,只要来了请求,就会自动开启线程来处理连接跟交互数据
    #第一个参数是绑定的地址(元组的形式),第二个参数传一个类(自己定义的类)
    duixiang = socketserver.ThreadingUDPServer(('172.17.8.49', 8080), Mytcp)#如果没有客户端连接就会在这里等待
    # #监听
    duixiang.serve_forever()#这里的serve_forever是一直监听的意思

客户端的不用修改,正常使用

最后效果如图