#server_tcp.py import socket sk = socket.socket() sk.bind(('127.0.0.1', 9999,)) sk.listen(5) # 接受客戶端的請求 while True: print('等待鏈接...') conn, address = sk.accept() # 會阻塞,接受客戶端的connect conn.sendall(bytes('歡迎致電XXX', encoding='utf-8')) while True: ret_bytes = conn.recv(1024) ret_str = str(ret_bytes, encoding='utf-8') if ret_str == 'q': break conn.sendall(bytes(ret_str + '好', encoding='utf-8')) print(conn) ------------------------------------------------ #client_tcp.py import socket obj = socket.socket() obj.connect(('127.0.0.1', 9999,)) # 鏈接 print("等待接收服務器數據中...") ret_bytes = obj.recv(1024) # 阻塞,若是接收不到消息就等着 print("接收到數據!") ret_str = str(ret_bytes, encoding='utf-8') print(ret_str) while True: inp = input('請輸入要發送的內容(q退出):') if inp == 'q': obj.sendall(bytes(inp, encoding='utf-8')) break else: obj.sendall(bytes(inp, encoding='utf-8')) ret_str = str(obj.recv(1024), encoding='utf-8') print(ret_str) obj.close() # 關閉
#server_udp.py import socket ip_port = ('127.0.0.1',9999) sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) sk.bind(ip_port) while True: data = sk.recv(1024) print data #client_udp.py import socket ip_port = ('127.0.0.1',9999) sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0) while True: inp = raw_input('數據:').strip() if inp == 'exit': break sk.sendto(inp,ip_port) sk.close()
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)服務器
參數一:地址簇網絡
socket.AF_INE:IPv4(默認)多線程
socket.AF_INET6:IPv6 併發
socket.AF_UNIX:只可以用於單一的Unix系統進程間通訊socket
參數二:類型Dtcp
socket.SOCK_STREAM:流式socket , for TCP (默認)函數
socket.SOCK_DGRAM:數據報式socket , for UDP編碼
socket.SOCK_RAW:原始套接字,普通的套接字沒法處理ICMP、IGMP等網絡報文,而SOCK_RAW能夠;其次,SOCK_RAW也能夠處理特殊的IPv4報文;此外,利用原始套接字,能夠經過IP_HDRINCL套接字選項由用戶構造IP頭。線程
socket.SOCK_RDM:是一種可靠的UDP形式,即保證交付數據報但不保證順序。SOCK_RDM用來提供對原始協議的低級訪問,在須要執行某些特殊操做時使用,如發送ICMP報文。SOCK_RDM一般僅限於高級用戶或管理員運行的程序使用。code
socket.SOCK_SEQPACKET:可靠的連續數據包服務
參數三:協議
0 (默認)與特定的地址家族相關的協議,若是是 0 ,則系統就會根據地址格式和套接類別,自動選擇一個合適的協議
sk.bind(address)
將套接字綁定到地址。address地址的格式取決於地址族。在AF_INET下,以元組(host,port)的形式表示地址。
sk.listen(backlog)
開始監聽傳入鏈接。backlog指定在拒絕鏈接以前,能夠掛起的最大鏈接數量。
若是backlog等於5,表示內核已經接到了鏈接請求,但服務器尚未調用accept進行處理的鏈接個數最大爲5。這個值不能無限大,由於要在內核中維護鏈接隊列。
sk.setblocking(bool)
是否阻塞(默認True)。若是設置False,那麼accept和recv時一旦無數據,則報錯。IO多路複用時會用到。
sk.accept()
接受鏈接並返回(conn,address),其中conn是新的套接字對象,能夠用來接收和發送數據。address是鏈接客戶端的地址。
接收TCP 客戶的鏈接(阻塞式)等待鏈接的到來
sk.connect(address)
客戶端鏈接到address處的套接字。通常,address的格式爲元組(hostname,port),若是鏈接出錯,返回socket.error錯誤。
sk.connect_ex(address)
同上,只不過會有返回值,鏈接成功時返回0,鏈接失敗時候返回編碼,例如:10061
sk.close()
關閉套接字
sk.recv(bufsize[,flag])
接受套接字的數據。數據以字符串形式返回,bufsize指定最多能夠接收的數量。flag提供有關消息的其餘信息,一般能夠忽略。
sk.recvfrom(bufsize[.flag])
與recv()相似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。
sk.send(string[,flag])
將string中的數據發送到鏈接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。即:可能未將指定內容所有發送。
sk.sendall(string[,flag])
將string中的數據發送到鏈接的套接字,但在返回以前會嘗試發送全部數據。成功返回None,失敗則拋出異常。
內部經過遞歸調用send,將全部內容發送出去。
sk.sendto(string[,flag],address)
將數據發送到套接字,address是形式爲(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。該函數主要用於UDP協議。
sk.settimeout(timeout)
設置套接字操做的超時期,timeout是一個浮點數,單位是秒。值爲None表示沒有超時期。通常,超時期應該在剛建立套接字時設置,由於它們可能用於鏈接的操做(如 client 鏈接最多等待5s )
sk.getpeername()
返回鏈接套接字的遠程地址。返回值一般是元組(ipaddr,port)。
sk.getsockname()
返回套接字本身的地址。一般是一個元組(ipaddr,port)
sk.fileno()
套接字的文件描述符
#server_file.py import socket sk = socket.socket() sk.bind(('127.0.0.1', 9999,)) sk.listen(5) # 接受客戶端的請求 while True: print('等待鏈接...') conn, address = sk.accept() # 會阻塞,接受客戶端的connect conn.sendall(bytes('歡迎登陸FTP',encoding='utf-8')) # 先接收文件大小 file_size = str(conn.recv(1024), encoding='utf-8') # 與client的接受配合解決粘包問題 conn.sendall(bytes('ack', encoding='utf-8')) total_size = int(file_size) has_recv = 0 f = open('new.png', 'wb') # 接收文件內容 while True: if total_size == has_recv: break data = conn.recv(1024) f.write(data) has_recv += len(data) f.close()
#client_file.py import socket import os obj = socket.socket() obj.connect(('127.0.0.1', 9999,)) # 鏈接 print("等待接收服務器數據中...") ret_bytes = obj.recv(1024) # 阻塞,若是接收不到消息就等着 print("接收到數據!") ret_str = str(ret_bytes, encoding='utf-8') print(ret_str) # 發送當前文件大小 size = os.stat('1.png').st_size size_bytes = bytes(str(size), encoding='utf-8') obj.sendall(size_bytes) # 發送完文件大小以後,執行接受(阻塞),當再次接受到服務器的數據時,就表示收到確認,而後纔開始發送文件內容,此法能夠用於解決粘包問題 obj.recv(1024) # 發送文件 with open('1.png', 'rb') as f: for line in f: obj.sendall(line) obj.close() # 關閉
TCP協議是面向流的協議,以流爲單位提取數據,這也是容易出現粘包問題的緣由
UDP是面向消息的協議,每一個UDP段都是一條消息,應用程序必須以消息爲單位提取數據
socketserver實現多併發=I/O多路複用+多線程
針對網絡交互實現的併發支持,server端代碼修改以下:
import socketserver class MyServer(socketserver.BaseRequestHandler): # 定義本身的類,必須繼承BaseRequestHandler類 def handle(self): # 必須使用方法名爲handle的方法 print("等待客戶端的鏈接..") conn = self.request # 客戶端的鏈接 conn.sendall(bytes('歡迎致電XXX', encoding='utf-8')) while True: ret_bytes = conn.recv(1024) ret_str = str(ret_bytes, encoding='utf-8') if ret_str == 'q': break conn.sendall(bytes(ret_str + '好', encoding='utf-8')) if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('127.0.0.1', 9999), MyServer) server.serve_forever() #其內部是一個while循環,不斷等待用戶鏈接