python socket(三)forking 實現網絡併發

python socket(一)python socket(二)中服務器都只能一個客戶端連接。

我們現在要讓服務器實現多個連接。

連接都是從conn,addr = s.accept()開始的,如果把連接寫成一個循環,就可以有多個連接啦。

# -*- coding: utf-8 -*-
##
# socket_server
# Created on July 7 2016
# @author:huangpingyi
##
import socket


HOST = ''  # 本地localhost
PORT = 50007
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)


while True:
       conn,addr = s.accept()#接收連接
       print "Connected by",addr
       while True:
           try:
               data = conn.recv(1024)
               if not data:
                   break
               print "Received",data
               msg = raw_input(">>:").strip()
               if len(msg) == 0:
                   continue
               conn.sendall(msg)
           except socket.error:
               break
       conn.close()

這樣就可以一個一個連接不斷接收,這樣就實現了一個併發。

但是需要一個連接然後結束進行另一個連接,但是服務器端是一直阻塞的,只能一個進行完下一個在進行。服務器處理客戶端發出請求時阻塞,沒有真正的實現同時爲多個客戶端服務。

python提供了3中方式:forking(多進程)、threading(多線程)、異步I/O

首先來看看用多進程的方式實現:

在Linux中提供了fork()函數,一個fork可以創建很多子進程。可以看看fork解析

啓動多個客戶端,看看服務器怎樣處理。

#! /usr/bin/env python

import os
import socket
import SocketServer
import threading
SERVER_HOST = 'localhost'
SERVER_PORT = 9999
BUF_SIZE = 1024
ECHO_MSG = 'Hello echo server!'

class ForkingClient():
   def __init__(self, ip, port):
       self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
       self.sock.connect((ip,port))
   def run(self):
       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
       response = self.sock.recv(BUF_SIZE)
       print "PID %s received: %s" %(current_process_id,response)
   def shutdown(self):
       self.sock.close()
class ForkingServerRequestHandler(SocketServer.BaseRequestHandler):
   def handle(self):
       data = 'Hello echo client!'
       current_process_id = os.getpid()
       response = '%s: %s' % (current_process_id,data)
       print "Server sending response : %s" %response
       self.request.send(response)
       return
class ForkingServer(SocketServer.ForkingMixIn,SocketServer.TCPServer,):
      pass

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

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

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

      server.shutdown()
      client1.shutdown()
      client2.shutdown()
      server.socket.close()

if __name__ =='__main__':
     main()


本實驗用SocketServer模塊提供好的一個類:ForkingMixIn。ForkingMinIn會爲每個客戶端請求派生一個進程,處理客戶端。

首先創建了一個客戶端類。

創建了一個ForkingServer類,繼承TCPServer和ForkingMixIn類。TCPServer實現了手動完成服務器的操作,例如創建套接字,綁定地址和監聽進入的連接。

還有一個處理請求類ForkingServerRequestHandler類,繼承SocketServer庫提供的BaseRequestHandler類.


在main函數裏,用setDeamon(True)設置一個守護線程。

創建兩個客戶端去請求服務器,可以看見服務器會啓用兩個進程分別處理客戶端的請求。



(注意:recv函數是阻塞函數,如果沒有返回值就會一直阻塞,直到連接關閉。可以通過設置一個時間超時settimeout( )來避免recv一直阻塞。

              在python中target一般爲一個可調用對象,比如函數,函數對象。args表示函數需要傳入的參數)

發佈了55 篇原創文章 · 獲贊 22 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章