對於python socket編程的初探

來源:互聯網
上載者:User

socket編程步驟

服務端建立一個socket,綁定地址和連接埠,然後監聽連接埠上傳入的串連,一旦有串連進來,就通過accept函數接收傳入的串連。

用戶端也是建立一個socket。綁定遠程地址和連接埠,然後建立串連,發送資料。

服務端socket

下面通過一段執行個體代碼來詳細說明 服務端 socker_server.py

import socketimport sysHOST = "127.0.0.1"               PORT = 10000              s = Nonefor res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC,                              socket.SOCK_STREAM, 0, socket.AI_PASSIVE):    af, socktype, proto, canonname, sa = res    try:        s = socket.socket(af, socktype, proto)    except socket.error as msg:        s = None        continue    try:        s.bind(sa)        s.listen(5)    except socket.error as msg:        s.close()        s = None        continue    breakif s is None:    print 'could not open socket'    sys.exit(1)conn, addr = s.accept()print 'Connected by', addrwhile 1:    data = conn.recv(1024)    if not data: break    conn.send(data)conn.close()

首先我們通過socket.getaddrinnfo函數將host/port轉換成一個包含5元組的序列。這個5元組包含我們建立一個socket串連所需要的所有必要參數。返回的5元組分別是 (family, sockettype, proto, canonname, sockaddr)

family 地址簇,用與socket()函數的第一個參數。主要有以下幾個

socket.AF_UNIX 用與單一機器下的進程通訊
socket.AF_INET 用與伺服器之間相互連信,通常都用這個。
socket.AF_INET6 支援IPv6
sockettype socket類型,用與socket()函數的第二個參數,常用的有

socket.SOCK_STREAM 預設,用於TCP協議
socket.SOCK_DGRAM 用於UDP協議
proto 協議,用於socket()函數的第三個參數。 getaddrinnfo函數會根據地址格式和socket類型,返回合適的協議

canonname 一個正常化的host name。

sockaddr 描述了一個socket address .是一個二元組,主要用於bind()和connect()函數

接下來建立一個socket對象,傳入getaddrinnfo函數返回的af,sockettype,proto。

s = socket.socket(af, socktype, proto)

然後綁定我的socket address

s.bind(sa)

開啟監聽模式

s.listen(5)

listen函數會監聽串連到socket上的串連,參數表示在拒絕串連之前系統可以掛起的最大串連隊列數量為5。這些串連還沒有被accept處理。數量不能無限大,通常指定5。

一旦我們監聽到了串連,就會調用accept函數接收串連

conn, addr = s.accept()

accept函數返回一個二元組,conn是一個新的socket對象,用來接收和發送資料。addr表示另一端的socket地址。

接下來我們就可以用conn對象發送和接收資料了

 data = conn.recv(1024) # 接收資料, 這裡指定一次最多接收的字元數量為1024 conn.send(data) # 發送資料

這裡我們接收到一個串連socket就會停止運行,所以如果要迴圈串連的話,將accept函數放入到一個死迴圈裡。

用戶端socket

用戶端socket編程相對比較簡單,通過connect和服務端建立串連之後,就可以相互連信了。socket_client.py如下

for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_STREAM):    af, socktype, proto, canonname, sa = res    try:        s = socket.socket(af, socktype, proto)    except socket.error as msg:        s = None        continue    try:        s.connect(sa)    except socket.error as msg:        s.close()        s = None        continue    breakif s is None:    print 'could not open socket'    sys.exit(1)s.sendall('Hello, world')data = s.recv(1024)s.close()print 'Received', repr(data)

以上主要是針對TCP流資料的socket編程。對於UDP協議的資料,處理略有不同。譬如發送接收UDP資料包處理函數為:

socket.sendto(string, flags, address)socket.recvfrom(bufsize[, flags]) #返回(string, address),string是返回的資料,address是發送方的socket地址

SocketServer模組

python中網路編程除了socket模組還提供了SocketServer模組,這一模組主要是對socket模組進行了封裝,將socket的對象的建立,綁定,串連,接收,發送,關閉都封裝在裡面,大大簡化了網路服務的編程。

此模組提供了以下2個主要的網路服務類,用於建立相應的通訊端流

TCPServer 建立TCP協議的通訊端流

UDPServer 建立UDP協議的通訊端流

我們有了通訊端流對象,還需要一個請求處理類。SocketServer模組提供了請求處理類有BaseRequestHandler,以及它的衍生類別StreamRequestHandler和DatagramRequestHandler。所以只要繼承這3個類中的一個,然後重寫handle函數,此函數將用來處理接收到的請求。下面看一個服務端的程式碼範例

import SocketServerclass MyTCPHandler(SocketServer.StreamRequestHandler):   """建立請求處理類,重寫handle方法。此外也可以重寫setup()和finish()來做一些請求處理前和處理後的一些工作"""    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", 10000    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)    # Activate the server; this will keep running until you    # interrupt the program with Ctrl-C    # server.shutdown()    server.serve_forever()  # 一直迴圈接收請求    # server.handle_request() # 只處理一次請求就退出

看著是不是代碼簡單了很多,而且SocketServer模組內部使用了多工IO技術,可以實現更好的串連效能。看serve_forever函數的原始碼用到了select模組。通過傳入socket對象調用select.select()來監聽socket對象的檔案描述符,一旦發現socket對象就緒,就通知應用程式進行相應的讀寫操作。原始碼如下:

def serve_forever(self, poll_interval=0.5):        """Handle one request at a time until shutdown.        Polls for shutdown every poll_interval seconds. Ignores        self.timeout. If you need to do periodic tasks, do them in        another thread.        """        self.__is_shut_down.clear()        try:            while not self.__shutdown_request:                # XXX: Consider using another file descriptor or                # connecting to the socket to wake this up instead of                # polling. Polling reduces our responsiveness to a                # shutdown request and wastes cpu at all other times.                r, w, e = _eintr_retry(select.select, [self], [], [],                                       poll_interval)                if self in r:                    self._handle_request_noblock()        finally:            self.__shutdown_request = False            self.__is_shut_down.set()

即使使用了select技術,TCPServer,UDPServer處理請求仍然是同步的,意味著一個請求處理完,才能處理下一個請求。但SocketServer模組提供了另外2個類用來支援非同步模式。

ForkingMixIn 利用多進程實現非同步

ThreadingMixIn 利用多線程實現非同步

看名字就知道使用了mixin模式。而mixin模式可以通過多繼承來實現,所以通過對網路服務類進行多繼承的方式就可以實現非同步模式

class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):    pass

針對ThreadindMixIn,實現非同步原理也就是在內部對每個請求建立一個線程來處理。看源碼

def process_request(self, request, client_address):        """Start a new thread to process the request."""        t = threading.Thread(target = self.process_request_thread,                             args = (request, client_address))        t.daemon = self.daemon_threads        t.start()

下面提供一個非同步模式的樣本

import socketimport threadingimport SocketServerclass ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):    def handle(self):        data = self.request.recv(1024)        cur_thread = threading.current_thread()        response = "{}: {}".format(cur_thread.name, data)        self.request.sendall(response)class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):    passdef client(ip, port, message):    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    sock.connect((ip, port))    try:        sock.sendall(message)        response = sock.recv(1024)        print "Received: {}".format(response)    finally:        sock.close()if __name__ == "__main__":    # Port 0 means to select an arbitrary unused port    HOST, PORT = "localhost", 0    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)    ip, port = server.server_address    # Start a thread with the server -- that thread will then start one    # more thread for each request    server_thread = threading.Thread(target=server.serve_forever)    # Exit the server thread when the main thread terminates    server_thread.daemon = True    server_thread.start()    print "Server loop running in thread:", server_thread.name    client(ip, port, "Hello World 1")    client(ip, port, "Hello World 2")    client(ip, port, "Hello World 3")    server.shutdown()    server.server_close()

以上是本人對socket相關的理解,有什麼不當或錯誤之處,還請指出。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.