day8--socketserver

socketserver分类:

1、TCP协议

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

2、UDP协议

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

剩下两种不常用协议如下:

calss socketserver.UnixStreamServer(server_address,RequestHandlerClass,bind_and_activate=True)

class socketserver.UnixDatagramServer(server_adddress,RequestHandlerClass,bind_and_activate=True)

协议之间的继承关系如下:

用socketServer创建一个服务的步骤:

1、创建一个request handler class(请求处理类),合理选择StreamRequestHandler和DatagramRequestHandler之中的一个作为父类(当然,便用BaseRequestHandler作为父类也可),并重写它的handler()方法;

2、实例化一个server class对象,并将服务的地址和之前创建的request handler class传递给它;

3、调用server class对象的handle_request()或server_forver()方法来开始处理请求,server_forever()是永久处理连接,使用这一个。

handle_request()只处理一个请求,处理完一个请求之后退出;server_forever()处理多个请求。

    与客户端每一个交互都是在handle()中处理的。

    socketServer其实是对客户端功能的进一步封装,比如bind(),listen(),accept()功能的封装,把这些功能统一分装到socketserver中,并且能够实现多线程,多进程的多并发的情况,客户端没有变化,下面来看一个简单的例子:

    socketserver写成的服务器端:

'''使用socketserver创建客户端'''
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    '''创建一个socketserver服务器接收端,首先实例化一个类'''
    def handle(self):
        self.data = self.request.recv(1024).strip()        #接收客户端发送的消息
        print("IP:%s,端口:%s链接进来了!!!"  %(self.client_address[0],self.client_address[1]))
        '''打印那个客户端端口连接过来了'''
        print(self.data.decode('utf-8'))    #打印接收到的消息
        self.request.send(self.data.upper())    #把接收到的消息大写返回回去


if __name__ == "__main__":
    HOST,PORT = 'localhost',9999
    server = socketserver.TCPServer((HOST,PORT),MyTCPHandler)
    '''创建连接实例'''
    server.serve_forever()

    上面代码中,我们创建了socketserver实例的服务器端,其实原理与socket都是一样的,不同的是,socketserver是对服务器端连接的封装,如个别bind()、listen()、accept()的封装,我们只需要self.request.recv(length).strip()接收数据即可,不用关心太多底层的问题,让创建socket的过程变得简单。

    socket创建的客户端:

'''创建一个socketserver客户端,客户端与socket客户端是完全一样的,只是服务器端不一样而已'''
import socket

client = socket.socket()
client.connect(("localhost",9999))
while True:
    mess = input("请输入您要发送的数据>>:")
    client.send(mess.encode('utf-8'))   #客户端发送数据给服务器端
    data = client.recv(1024)    #客户端接收服务器发送过来的数据
    print("接收到的数据:",data.decode('utf-8'))

    上面代码是socket创建的客户端,就是一个简单的收发数据,在这里能够连续发送数据,下面来首先启动服务器端,接着启动客户端进行交互。

运行如下:

请输入您要发送的数据>>:dfasfdasd
接收到的数据: DFASFDASD
请输入您要发送的数据>>:1
接收到的数据:
请输入您要发送的数据>>:das
Traceback (most recent call last):
  File "/home/zhuzhu/第八天/socket_server/socketserver_client.py", line 8, in <module>
    client.send(mess.encode('utf-8'))   #客户端发送数据给服务器端
BrokenPipeError: [Errno 32] Broken pipe
    从上面可以看出,第一次交互是没有问题的,第二次交互就开始出错了,为什么呢?我们来看下服务器端,在服务器端里面,handle()里面每次只能接收一次数据,因此会出现错误的情况,下面我们来让服务器端实现连续交互,修改服务器端,如下:

'''使用socketserver创建客户端'''
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    '''创建一个socketserver服务器接收端,首先实例化一个类'''
    def handle(self):
        while True:
            self.data = self.request.recv(1024).strip()        #接收客户端发送的消息
            if not self.data:
                '''如果接收的为空,说明服务器端发送的是空的数据,或者是服务器端断开了'''
                print(self.client_address,"客户端断开了!!!")
                break
            print("IP:%s,端口:%s链接进来了!!!"  %(self.client_address[0],self.client_address[1]))
            '''打印那个客户端端口连接过来了'''
            print(self.data.decode('utf-8'))    #打印接收到的消息
            self.request.send(self.data.upper())    #把接收到的消息大写返回回去


if __name__ == "__main__":
    HOST,PORT = 'localhost',9997
    server = socketserver.TCPServer((HOST,PORT),MyTCPHandler)
    '''创建连接实例'''
    server.serve_forever()

    上面代码中,对服务器进行了修改,让服务器能够循环接收数据,这样客户端就能无限发送和接收数据了,不过要牢记一点,客户端是不能够发送空的消息给服务器的,如果发送空的消息给服务器,就会卡主,传递不了消息,因而要设定发送空消息的指令。

    上面代码修改了,能够实现交互,但是我们发现了一个问题,服务器还是只能每次跟一个客户端交互,不能同时跟多个客户端交互,其他客户端必须等待本次交互完成之后,在进行交互,这与socket写的服务器和客户端没有区别,如何用socketserver实现多并发呢,下面有两种方式,一种是多线程,如下:

    (1)多线程

'''使用socketserver创建客户端'''
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    '''创建一个socketserver服务器接收端,首先实例化一个类'''
    def handle(self):
        while True:
            self.data = self.request.recv(1024).strip()        #接收客户端发送的消息
            if not self.data:
                '''如果接收的为空,说明服务器端发送的是空的数据,或者是服务器端断开了'''
                print(self.client_address,"客户端断开了!!!")
                break
            print("IP:%s,端口:%s链接进来了!!!"  %(self.client_address[0],self.client_address[1]))
            '''打印那个客户端端口连接过来了'''
            print(self.data.decode('utf-8'))    #打印接收到的消息
            self.request.send(self.data.upper())    #把接收到的消息大写返回回去


if __name__ == "__main__":
    HOST,PORT = 'localhost',9997
    server = socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler)
    '''创建连接实例'''
    server.serve_forever()

    多线程就是修改server连接的方式,socketserver.ThreadingTCPServer即可,如下:

    server = socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler)

    上面修改之后,就能实现多线程多并发,我们可以同时启动多个客户端进行交互。

    上面启动一个服务器,多个客户端,交互如下:

客户端1:
请输入您要发送的数据>>:这么神奇
接收到的数据: 这么神奇
客户端2:
请输入您要发送的数据>>:alex
接收到的数据: ALEX
客户端3:
请输入您要发送的数据>>:sb
接收到的数据: SB
客户端4:
请输入您要发送的数据>>:yes
接收到的数据: YES
服务器: IP:127.0.0.1,端口:54856链接进来了!!! 这么神奇 IP:127.0.0.1,端口:54858链接进来了!!! alex IP:127.0.0.1,端口:54860链接进来了!!! sb IP:127.0.0.1,端口:54862链接进来了!!! yes

    上面可以看出,我们实现了多线程多并发的交互,我们也可以实现多进程的交互。

    (2)多进程

    多进程和多线程是一样的,也是修改服务器端的交互指令即可,如下:

    server = socketserver.ForkingTCPServer((HOST,PORT),MyTCPHandler)

    上面,ForKingTCPServer((IP,端口号),实例化的类),就实现了和多线程一个的多并发情况,如下:

'''使用socketserver创建客户端'''
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    '''创建一个socketserver服务器接收端,首先实例化一个类'''
    def handle(self):
        while True:
            self.data = self.request.recv(1024).strip()        #接收客户端发送的消息
            if not self.data:
                '''如果接收的为空,说明服务器端发送的是空的数据,或者是服务器端断开了'''
                print(self.client_address,"客户端断开了!!!")
                break
            print("IP:%s,端口:%s链接进来了!!!"  %(self.client_address[0],self.client_address[1]))
            '''打印那个客户端端口连接过来了'''
            print(self.data.decode('utf-8'))    #打印接收到的消息
            self.request.send(self.data.upper())    #把接收到的消息大写返回回去


if __name__ == "__main__":
    HOST,PORT = 'localhost',9997
    server = socketserver.ForkingTCPServer((HOST,PORT),MyTCPHandler)
    '''创建连接实例'''
    server.serve_forever()

    交互如下:

客户端1:
请输入您要发送的数据>>:dfasfdas
接收到的数据: DFASFDAS
客户端2:
请输入您要发送的数据>>:xiaozhuzhu
接收到的数据: XIAOZHUZHU
客户端3:
请输入您要发送的数据>>:pangshini
接收到的数据: PANGSHINI
客户端4:
请输入您要发送的数据>>:dapagnzhi
接收到的数据: DAPAGNZHI
服务器:
IP:127.0.0.1,端口:54876链接进来了!!!
dfasfdas
IP:127.0.0.1,端口:54878链接进来了!!!
xiaozhuzhu
IP:127.0.0.1,端口:54880链接进来了!!!
pangshini
IP:127.0.0.1,端口:54886链接进来了!!!
dapagnzhi

    上面就是多进程下的多并发的交互情况,和多线程交互结果是一致的,不同的是,ForKingTCPServer()多进程在Windows下会报错,由于Windows多进程和Linux多进程的原理是不一样的。在Linux下没有问题,在Windows下会报错。

    基本的socketserver代码:

 

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    """
    The request handler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """

    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        # just send back the same data, but upper-cased
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999

    # Create the server, binding to localhost on port 9999
    server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)

    # Activate the server; this will keep running until you
    # interrupt the program with Ctrl-C
    server.serve_forever()

 

    总结:

    (1)socketserver与socket是一样的,都是为了实现客户端和服务器端的交互;

    (2)socketserver能够实现多并发,而socket只能一对一的进行交互;

    (3)socketserver.TCPServer是实现与socket一样的一对一交互,socketserver.ThreadingTCPServer是实现多线程下的多并发,socketserver.ForkingTCPServer是实现多进程的下的多并发的数据交互,都是在TCP协议下进行交互的,常用的还有UDP协议。

    (4)客户端不能发送空的消息,服务器端也不能接收空的消息

    (5)如果服务器端接收的消息为空,说明客户端已经退出了,不然若客户端发送为空,则造成堵塞;

    (6)socketserver是对socket中bind,listen,accept()的封装。

posted @ 2017-08-24 06:20  (野生程序员)  阅读(463)  评论(0编辑  收藏  举报