[Python] ServerSocket

最後這篇來講server socket在python上最後一個也是最方便的module

Python SocketServer: http://docs.python.org/2/library/socketserver.html

官網算是有一些範例, 而且網路上也有一隻影片有簡易教學

youtube 英文教學: http://www.youtube.com/watch?v=OU7AWyw8MfI

基本上SocketServer是大幅簡化了我們要寫Socket Server的module,

他基本支援四種server classes TCPServer, UDPServer, UnixStreamServer, UnixDatagramServer

前兩種很常見, 而後兩種是純Linux使用, 差在一個起始是AF_INET還是AF_UNIX

而SocketServer module如果在預設的情況下是同步的設定, 也就是一個服務處理一個connection

那如果你有多個需求或者講非同步的處理, 可能有的在處理連線有的在處理其他工作, 就可以使用ThreadingMixIn or ForkingMixIn 來幫你解決這類問題

所以要建立一個SocketServer, 有幾個必要步驟

1. 因為既然要寫SocketServer, 就會有相對應的服務要處理, 所以要先建立一個request handler去繼承RequestHandler (這個有幾種種類), 用來實作你要怎麼處理進入的要求 

2. 當然你也要有建立一個上述所說的socket class, 並且附上剛剛建立的request handler

3. 有了前面兩個設定之後, 就可以呼叫andle_request() or serve_forever()來幫你開始SocketServer的服務

看起來內容很多而且不小心寫的太像翻譯, 用一點例子輔助說明, 第一個範例我拿官網的解釋

import SocketServer

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print "{} wrote:".format(self.client_address[0])
        print self.data
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()

我拿掉註解, 首先這程式很小, 而且照剛剛三步驟就真的寫完了

1. 有一個MyTCPHandler去繼承BaseRequestHandler(有其他更好用的後續提), 然後實作handler要做甚麼就可以, 這邊很單純, 他收client端傳過訊息, 印出傳過來的訊息之後, 再把這訊息變大寫傳出去, 而且用sendall, 所以所有有連線的client都會收到此訊息大寫版

2. 建立SocketServer.TCPServer((HOST, PORT), MyTCPHandler), 這件事情會建立socket也會將剛剛的request handler傳入

3. 呼叫serve_forever, 程式就開始跑了 (想成之前範例的while True)

幾個東西稍微說明一下

self.client_address就是client的位址, 跟當初的accpet()回傳的結果是一樣的

self.request其實就是當初用socket建立出來的socket物件, 所以也可以用self.request.getpeername()去取得client位址, 跟上述一樣

超簡單的範例, 不過, 因為太簡單, 有很多事情還不太方便, 例如說, 我想要避免之前因為中斷結果又會造成connection問題,  或者是需要threading, 所以下面再提供一個稍微小複雜的範例

import SocketServer, sys, threading
from time import ctime

HOST= ''
PORT = 12345

class MyServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    daemon_threads = True
    allow_reuse_address = True

class MyHandler(SocketServer.StreamRequestHandler):
    def handle(self):
        cur = threading.current_thread()
        print '[%s] Client connected from %s and [%s] is handling with him.' % (ctime(), self.request.getpeername(), cur.name)
        while True:
            msg = self.request.recv(1024).strip()
            if not msg:
                pass
            else:
                print "Client send: " + msg 
                self.wfile.write("You say: " + msg + "\r\n")
        self.request.close()

if __name__ == '__main__':
    server = MyServer((HOST,PORT), MyHandler)
    ip, port = server.server_address
    print "Server is starting at:", (ip, port)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        sys.exit(0)

這範例一樣很笨, 只是在server紀錄一下誰連過來, 然後跟他問聲好XD

不過我額外連Server端都重新繼承了, 除了繼承了ThreadingMixIn之外, 我設定了兩件事情

1. daemon_thread = True, 也就是當main thread死, 其餘thread都跟著死, 不設定開的話, 如果用ctrl+C去中止程式是中止不了的, 以server來講, server thread都中止了, 剩下的理應也沒有理由活著了 (要死一起死!!)

2. allow_reuse_address = True, 你看多貼心, 這次讓你可以設定是不是要reuse address

那我有在main那邊簡單做個try~except, 只是要抓ctrl+C的命令, 有的話就程式結束

而在Handler那端, 拿前幾篇的範例使用, 不過多做了一些訊息log, 像這邊有一個threading.current_thread

這個會取得在這時候運作的thread基本資訊, 例如名稱之類的, 可以拿來做簡易的log訊息

補充: ThreadingMixIn跟ForkingMixIn差異是, Threading是每次呼叫個thread出來做事情, Forking是每次都會產生個process出來做事情, 不懂差異的要去查一下thread v.s process

其餘不錯的SocketServer範例教學: http://kmkeen.com/socketserver/2009-02-07-07-52-15.html

以上

留言

  1. 請問若是server要對外開放(其他電腦能夠連上server),需要作什麼樣的修改?
    對於這塊不是很有概念@@
    請指教了 謝謝

    回覆刪除
    回覆
    1. 簡單來說, 如果HOST設定localhost, 那就只有本機自己才連的到, 如果HOST設定空字串, 照理講就可以開放給對外連線了, 只是要確定防火牆沒有擋住

      刪除

張貼留言

這個網誌中的熱門文章

[Linux] Linux下查詢硬體記憶體資訊 Memory Information

[Other] Chrome 重新整理所有開啟頁面

[Python] Simple Socket Server