1. 程式人生 > >46. Python Socket編程

46. Python Socket編程

socket

復習:消息隊列

為了防止消息丟失,或者是調用方,不需一直等待響應方的結果。

# threadtest.py

import codecs
from queue import Queue
from threading import Thread

import time


class Produce(Thread):
    def __init__(self, queue):
        super(Produce, self).__init__()
        self.fileName = "../firstlession/passwd"
        self.fileList = list()
        self.queue = queue
    def run(self):
        with codecs.open(self.fileName) as f:
            self.fileList += f.readlines()
        for line in self.fileList:
            self.queue.put(line)

class Consumer(Thread):
    def __init__(self, queue):
        self.queue = queue
        super(Consumer, self).__init__()
        self.newPasswd = "newpasswd.txt"
        self.fileList = list()
        self.stat = 1
    def run(self):
        while 1:
            if self.queue.empty():
                time.sleep(2)
                self.stat += 1
                if self.stat == 5:
                    break
            else:
                self.stat = 1
                data = self.queue.get()
                self.fileList.append(data)

        with codecs.open(self.newPasswd, 'w') as f:
            f.writelines(self.fileList)


調用

# thread1.py

from queue import Queue
from onlive.secondlesson.threadtest import Produce, Consumer

def main():
    q = Queue()
    produce = Produce(q)
    consumer = Consumer(q)
    produce.start()
    consumer.start()

if __name__ == '__main__':
    main()


socket 簡介

TCP的可靠性實現:

(1)校驗碼

(2)接收方反饋

(3)信息包附帶序號


UDP:

(1)快 不需要花費時間建立和關閉連接

(2)快 偶爾丟失一兩個消息包無所謂,但是TCP會嚴格檢查

(3)快 UDP的限制是一個信息包不超過64KB的數據


TCP和UDP區別:

UDP不建立連接,只保證數據的完整性,數據傳輸快,但是不保證數據是否真的被收到,也不保證數據是否只接收一次,也不保證次序。

TCP則相反。

服務端是用來給一個或多個客戶端提供服務的,當客戶端發起請求,開始等待服務端的返回結果,服務端接受完請求以後,根據自己的邏輯處理請求,並返回給客戶端,客戶端接收到返回結果以後,關閉和服務端的連接。

[備註]:只要是發送數據的就是"寫",只要是接收數據的就是"讀"。

最常用的客戶端和服務端有兩種模式:C/S模式(mysql) 和 B/S模式(百度、京東、淘寶網站等)


socket流程:

(1)服務端創建完一個socket以後

(2)需要綁定一個IP:PORT

(3)對其進行監聽(listen)-->【監聽的方法內需要帶一個數字,這個數字表示同時有多少個客戶端可以來訪問服務端】

(4)然後接收請求(accept)--> 【客戶端每創建一個連接,調用 "connect函數" 後,服務端就需要生成一個新的socket連接和客戶端進行傳輸,傳輸完成後關閉客戶端連接、關閉服務端;服務端永遠要比客戶端多一個socket連接,如果說客戶端是n個socket連接,那麽服務端就要創建n+1個socket連接,因為剛開始啟動服務端的時候,服務端就要創建一個socket連接,每當從客戶端過來一個連接,服務端就要創建一個socket連接跟客戶端進行交互,所以服務端比客戶端多一個連接。】

技術分享圖片


socket常用函數講解:

創建套接字:

s = socket.socket(address family, socket type)


address family:

socket.AF_INET 默認ipv4

socket.AF_INET6 ipv6

socket.AF_UNIX 只用於單一unix系統間進行通信

socket type:

socket.SOCK_STREAM 流式socket TCP

socket.SOCK_DGRAM 數據報式socket UDP


TCP 方式 socket:

(1)創建socket:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

(2)綁定地址:

address = ('0.0.0.0', 8009)

s.bind(address) 或者 s.bind((0.0.0.0, 8009)) 兩種方式相同;

【註意:address 必須是一個元組,容易錯誤,address = (host, port)】

【host:服務端ip,字符串類型,如果為0.0.0.0,代表本機的任意一個IP】

【port:服務端提供的端口,整形,0-1024為系統保留(不選這裏面的端口)】

(3)監聽消息:

s.listen(badklog)

backlog 代表可以同時接受多少個socket連接

(4)接受連接:

conn, addr = s.accept()

接受連接並返回元組(conn,addr),其中conn是新的套接字對象,每個新的連接就創建一個新的對象。可以用來接受和發送數據,addr是客戶端的地址:包含host和port。

(5)發送數據:

s.send(string) 發送字符串到連接的套接字,可能未將指定內容全部發送;

s.sendall(string) 內部遞歸調用send,將所有內容發送出去,建議使用。

(6)接收數據:

data = s.recv(bufsize)

接收套接字數據,數據以字符串形式返回,bufsize指定最多接收的數據量,可以使用1024, 2048

如果不知道接收的數量有多少,可以能幾個字節,可能幾兆,一般通過循環接收。


UDP 方式 socket:

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

s.sendto(string)

data, address = s.recvfrom(bufsize)


客戶端:

客戶端首先也要創建socket套接字

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


客戶端連接服務端函數:

s.connect(address) #連接到address的套接字

result - connect_ex(address) #成功返回0,失敗返回錯誤碼


通用:

s.close() #關閉套接字

s.getsocketname() #獲取套接字的名字

s.settimeout(timeout) #設置套接字超時時間,timeout為float類型,單位秒

s.gettimeout() #獲得套接字超時時間

s.setblocking(flag) #flage為bool值

setblocking(True) is equivalent to settimeout(None); #不設置超時時間,一直阻塞在那裏

setblocking(False) is equivalent to settimeout(0.0); #設置超時時間為0,如果設置False,accept和recv一旦無數據,則報錯。

s.fileno() # 返回套接字的文件描述符(一個小整數)。這對於select.select()是有用的。


socket例子:


寫socket工具:

# util.py

import socket
import time


class InitSocketTest(object):
    def __init__(self, host, port, type):
        self.host = host
        self.port = port
        self.address = (host, port)
        self.type = type
        self.s = None
        self.creatsocket()

    def creatsocket(self):
        if self.type.upper() == "TCP":
            self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        elif self.type.upper == "UDP":
            self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        else:
            print("you must input the InitSocket(type) is 'UDP|TCP' ")

class SocketServerTest(InitSocketTest):
    def __init__(self, host, port, type,  backlog):
        self.backlog = backlog
        super(SocketServerTest, self).__init__(host, port, type)
        self.clientAddress = None

    def run(self):
        self.s.bind(self.address)
        self.s.listen(self.backlog)
        print("server starting…………")
        conn, self.clientAddress = self.s.accept()
        print("accept connect from {0}".format(self.clientAddress))
        for i in range(1, 10):
            conn.sendall("i = {0}".format(str(i)).encode("utf-8"))
        self.s.close()

class ClientSocketTest(InitSocketTest):
    def run(self):
        self.s.connect(self.address)
        stat = 1
        while 1:
            data = self.s.recv(2048)
            if len(data)>0:
                stat = 1
                print(data.decode("utf-8"))
            else:
                stat += 1
                time.sleep(1)
                if stat == 5:
                    break


# testserver.py

from onlive.sockettest.util import SocketServerTest    
if __name__ == '__main__':    
    socketServer = SocketServerTest(host="0.0.0.0", port=9999, type="tcp", backlog=5)    
    socketServer.run()


# testclient.py

from onlive.sockettest.util import ClientSocketTest    
if __name__ == '__main__':    
    socketClient = ClientSocketTest(host="127.0.0.1", port=9999, type="tcp")    
    socketClient.run()


46. Python Socket編程