使用多路复用套接字I/O提升性能之——ForkingMixIn 《Python网络编程攻略》

前一例不同,本次考虑多个客户端连接服务器的情况,且可以异步通信。

服务器不需要在阻塞模式中处理客户发出的请求,而是单独处理每个请求。如果某个客户端接受或处理数据花了很长时间,服务器无需等待处理完成,即可使用另外的线程和其他客户端进行通信。

实现方法

  1. SocketServer模块:利用SocketServer模块提供的类可以直接实现TCP、UDP及其他协议服务器。
  2. ForkingMixIn类,用于异步处理客户端。
  3. 我们创建ForkingServer类,继承TCPServer和ForkingMixIn类。继承TCPServer可以省去手动创建服务器操作,如创建套接字、绑定地址和监听连接等;继承ForkingMinIn用于异步处理。
  4. ForkingServer类还要创建一个请求处理程序ForkingServerRequestHandler(继承自SocketServer库中的BaseRequestHandler类),说明如何处理客户端请求。
  5. 客户端ForkingClient使用面向对象的方式编写。

代码 2_1_forking_mixin_socket_server.py

'''
Created on 2017-2-28

@author: lenovo
'''
import os
import socket
import threading
import SocketServer

SERVER_HOST = 'localhost'
SERVER_PORT = 0 # Tells the kernel to pick up a port dynamically
BUF_SIZE = 1024
ECHO_MSG = 'Hello echo server!'

class ForkingClient():
    """ A client to test forking server"""
    def __init__(self,ip,port):
        # Create a socket
        self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.sock.connect((ip,port))

    def run(self):
        """Client playing with the server"""
        #send the data to server
        current_process_id = os.getpid()
        print 'PID %s Sending echo message to the server : "%s"' %(current_process_id,ECHO_MSG)
        sent_data_length = self.sock.send(ECHO_MSG)
        print "Sent: %d characters, so far..." %sent_data_length

        # Display server response
        response = self.sock.recv(BUF_SIZE)
        print "PID %s received: %s" %(current_process_id,response[5:])

    def shutdown(self):
        self.sock.close()

class ForkingServerRequestHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        # Send the echo back to the client
        data = self.request.recv(BUF_SIZE)
        current_process_id = os.getpid()
        response = '%s: %s' % (current_process_id,data)
        print "Server sending response [current_process_id: data] = [%s]" %response
        self.request.send(response)
        return
class ForkingServer(SocketServer.ForkingMixIn,SocketServer.TCPServer,):
    """Nothing to add here, inherited everything necessary form parents."""
    pass

def main():
    #Launch the server
    server = ForkingServer((SERVER_HOST,SERVER_PORT),ForkingServerRequestHandler)
    ip,port = server.server_address #Retrive the port number
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.setDaemon(True)
    server_thread.start()
    print 'Server loop running PID: %s' %os.getpid()

    #Launch the client
    client1 = ForkingClient(ip,port)
    client1.run()

    client2 = ForkingClient(ip,port)
    client2.run()

    #clean them up
    server.shutdown()
    client1.shutdown()
    client2.shutdown()
    server.socket.close()
if __name__ == '__main__':
    main()

运行结果

xx@ubuntu:~$ python 2_1_forking_mixin_socket_server.py 
Server loop running PID: 2545
PID 2545 Sending echo message to the server : "Hello echo server!"
Sent: 18 characters, so far...
Server sending response [current_process_id: data] = [2547: Hello echo server!]
PID 2545 received:  Hello echo server!
PID 2545 Sending echo message to the server : "Hello echo server!"
Sent: 18 characters, so far...
Server sending response [current_process_id: data] = [2548: Hello echo server!]
PID 2545 received:  Hello echo server!
xx@ubuntu:~$

原理分析

主线程中创建了一个ForkingServer实例,作为守护进程在后台运行,然后再创建两个客户端与服务器交互。

不难发现,服务器每次响应请求的进程是不同,一个是2547,一个是2548,可见服务器对每个客户端请求生成了不同的进程去去处理。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
多路 I/O 复用可以通过 select 或者 epoll 等技术实现同时监听多个套接字的数据。以下是使用 select 监听 3 个套接字的数据的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/select.h> #define PORT1 8000 #define PORT2 8001 #define PORT3 8002 int main() { int sockfd1, sockfd2, sockfd3, maxfd; fd_set readfds; struct sockaddr_in addr1, addr2, addr3; char buffer[1024]; // create socket 1 sockfd1 = socket(AF_INET, SOCK_STREAM, 0); memset(&addr1, 0, sizeof(addr1)); addr1.sin_family = AF_INET; addr1.sin_port = htons(PORT1); addr1.sin_addr.s_addr = htonl(INADDR_ANY); bind(sockfd1, (struct sockaddr *)&addr1, sizeof(addr1)); listen(sockfd1, 5); // create socket 2 sockfd2 = socket(AF_INET, SOCK_STREAM, 0); memset(&addr2, 0, sizeof(addr2)); addr2.sin_family = AF_INET; addr2.sin_port = htons(PORT2); addr2.sin_addr.s_addr = htonl(INADDR_ANY); bind(sockfd2, (struct sockaddr *)&addr2, sizeof(addr2)); listen(sockfd2, 5); // create socket 3 sockfd3 = socket(AF_INET, SOCK_STREAM, 0); memset(&addr3, 0, sizeof(addr3)); addr3.sin_family = AF_INET; addr3.sin_port = htons(PORT3); addr3.sin_addr.s_addr = htonl(INADDR_ANY); bind(sockfd3, (struct sockaddr *)&addr3, sizeof(addr3)); listen(sockfd3, 5); // set up file descriptor set FD_ZERO(&readfds); FD_SET(sockfd1, &readfds); FD_SET(sockfd2, &readfds); FD_SET(sockfd3, &readfds); maxfd = sockfd3 + 1; while (1) { fd_set tmpfds = readfds; int ret = select(maxfd, &tmpfds, NULL, NULL, NULL); if (ret < 0) { perror("select"); exit(EXIT_FAILURE); } else if (ret == 0) { continue; } if (FD_ISSET(sockfd1, &tmpfds)) { struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); int clientfd = accept(sockfd1, (struct sockaddr *)&client_addr, &client_len); printf("new connection from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); FD_SET(clientfd, &readfds); if (clientfd >= maxfd) { maxfd = clientfd + 1; } } if (FD_ISSET(sockfd2, &tmpfds)) { struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); int clientfd = accept(sockfd2, (struct sockaddr *)&client_addr, &client_len); printf("new connection from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); FD_SET(clientfd, &readfds); if (clientfd >= maxfd) { maxfd = clientfd + 1; } } if (FD_ISSET(sockfd3, &tmpfds)) { struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); int clientfd = accept(sockfd3, (struct sockaddr *)&client_addr, &client_len); printf("new connection from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); FD_SET(clientfd, &readfds); if (clientfd >= maxfd) { maxfd = clientfd + 1; } } for (int i = sockfd3 + 1; i < maxfd; i++) { if (FD_ISSET(i, &tmpfds)) { int n = recv(i, buffer, sizeof(buffer), 0); if (n <= 0) { close(i); FD_CLR(i, &readfds); printf("connection closed\n"); } else { buffer[n] = '\0'; printf("received data from %d: %s\n", i, buffer); } } } } close(sockfd1); close(sockfd2); close(sockfd3); return 0; } ``` 这段代码同时监听了 3 个套接字,当有客户端连接时,会打印出客户端的 IP 地址和端口号。当有数据到达时,会打印出数据和对应的套接字文件描述符。当连接关闭时,会打印出连接关闭的信息。可以根据自己的需要,修改代码以满足实际需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值