Python網絡編程篇之socket
1 socket
插座?呵呵,想多了,翻譯過來意思是套接字!
A network socket is an internal endpoint for sending or receiving data at a single node in a computer network. Concretely, it is a representation of this endpoint in networking software (protocol stack), such as an entry in a table (listing communication protocol, destination, status, etc.), and is a form of system resource.
參數一:地址簇
socket.AF_INET IPv4(默認)
socket.AF_INET6 IPv6
socket.AF_UNIX 只能夠用於單一的Unix系統進程間通信
參數二:類型
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_RAM用來提供對原始協議的低級訪問,在需要執行某些特殊操作時使用,如發送ICMP報文。SOCK_RAM通常僅限於高級用戶或管理員運行的程序使用。
socket.SOCK_SEQPACKET 可靠的連續數據包服務
參數三:協議
0 (默認)與特定的地址家族相關的協議,如果是 0 ,則系統就會根據地址格式和套接類別,自動選擇一個合適的協議
3.面向連接的套接字也稱為虛擬電路或流套接字
面向連接的通信提供序列化的、可靠的和不重復的數據交付,並且沒有記錄邊界,實現這種連接的主要協議是TCP(傳輸控制協議)。
創建TCP套接字必須使用SOCK_STREAM作為套接字類型。
無連接的傳輸無法保證傳輸的內容的順序性、可靠性。無連接傳輸的優勢是沒有維護虛擬電路連接帶來的開銷,從而擁有更低的成本。實現無連接的主要協議是UDP(用戶數據報協議),創建UDP套接字必須使用SOCK_DGRAM作為套接字類型。
4.Socket 參數介紹
sk.bind(address) s.bind(address) 將套接字綁定到地址。address地址的格式取決於地址族。在AF_INET下,以元組(host,port)的形式表示地址。 sk.listen(backlog) 開始監聽傳入連接。backlog指定在拒絕連接之前,可以掛起的最大連接數量。 backlog等於5,表示內核已經接到了連接請求,但服務器還沒有調用accept進行處理的連接個數最大為5 這個值不能無限大,因為要在內核中維護連接隊列 sk.setblocking(bool) 是否阻塞(默認True),如果設置False,那麽accept和recv時一旦無數據,則報錯。 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() 套接字的文件描述符
5.創建socket
socket.socket(AddressFamily, Type)
函數 socket.socket 創建?個 socket, 返回該 socket 的描述符, 該函數帶有兩個參數:
Address Family: 可以選擇 AF_INET( ?於 Internet 進程間通信) 或者AF_UNIX( ?於同?臺機器進程間通信) ,實際?作中常?AF_INET
Type: 套接字類型, 可以是 SOCK_STREAM( 流式套接字, 主要?於TCP 協議) 或者 SOCK_DGRAM( 數據報套接字, 主要?於 UDP 協議)創建?個tcp socket( tcp套接字)
5.1創建?個tcp socket( tcp套接字)
TCP通信需要建立一個可靠連接的過程,而且通信雙方以流的形式發送數據。
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print ‘Socket Created‘
tcp_server
# -*- coding: utf-8 -*- # 2017/11/25 16:31 import socket import threading import time def dealClient(sock, addr): #第四步:接收傳來的數據,並發送給對方數據 print(‘Accept new connection from %s:%s...‘ % addr) sock.send(b‘Hello,I am server!‘) while True: data = sock.recv(1024) time.sleep(1) if not data or data.decode(‘utf-8‘) == ‘exit‘: break print(‘-->>%s!‘ % data.decode(‘utf-8‘)) sock.send((‘Loop_Msg: %s!‘ % data.decode(‘utf-8‘)).encode(‘utf-8‘)) #第五步:關閉套接字 sock.close() print(‘Connection from %s:%s closed.‘ % addr) if __name__=="__main__": #第一步:創建一個基於IPv4和TCP協議的Socket # 套接字綁定的IP(127.0.0.1為本機ip)與端口 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((‘127.0.0.1‘, 9999)) #第二步:監聽連接 s.listen(5) print(‘Waiting for connection...‘) while True: # 第三步:接受一個新連接: sock, addr = s.accept() # 創建新線程來處理TCP連接: t = threading.Thread(target=dealClient, args=(sock, addr)) t.start()
tcp_client
# -*- coding: utf-8 -*- # 2017/11/25 16:32 import socket #初始化Socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #連接目標的ip和端口 s.connect((‘127.0.0.1‘, 9999)) # 接收消息 print(‘-->>‘+s.recv(1024).decode(‘utf-8‘)) # 發送消息 s.send(b‘Hello,I am a client‘) print(‘-->>‘+s.recv(1024).decode(‘utf-8‘)) s.send(b‘exit‘) #關閉套接字 s.close()
5.2創建?個udp socket( udp套接字)
使用UDP協議時,不需要建立連接,只需要知道對方的ip和port,就可以直接發數據包,但是不關心是否能到達目的端。
UDP --- 用戶數據報協議, 是一個無連接的簡單的面向數據報的運輸層協議。
UDP不提供可靠性, 它只是把應用程序傳給IP層的數據報發送出去,但是並不能保證它們能到達目的地。
由於UDP在傳輸數據報前不用在客戶和服務器之間建立一個連接,且沒有超時重發等機制,傳輸速度很快。
UDP是一種面向無連接的協議, 每個數據報都是一個獨立的信息,包括完整的源地址或目的的地址,
它在網絡上以任何可能的路徑傳往目的地, 因此能否到達?的地, 到達目的地的時間以及內容的正確性都是不能被保證的。
import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) print ‘Socket Created‘
udp_server
# -*- coding: utf-8 -*- # 2017/11/25 16:38 import socket #創建Socket,綁定指定的ip和端口 #SOCK_DGRAM指定了這個Socket的類型是UDP。綁定端口和TCP一樣。 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((‘127.0.0.1‘, 9999)) print(‘Bind UDP on 9999...‘) while True: # 直接發送數據和接收數據 data, addr = s.recvfrom(1024) print(‘Received from %s:%s.‘ %(addr,data)) s.sendto(b‘Hello‘,addr)
udp_client
# -*- coding: utf-8 -*- # 2017/11/25 16:39 import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) for data in [b‘Hello‘, b‘World‘]: # 發送數據: s.sendto(data, (‘127.0.0.1‘, 9999)) # 接收數據: print(s.recv(1024).decode(‘utf-8‘)) s.close()
一下均基於tcp開發:
一對一
server
#__author: greg #date: 2017/9/16 16:11 import socket ip_port = (‘127.0.0.1‘,8888) sk = socket.socket() sk.bind(ip_port) sk.listen(5)#最大排隊數,能開多少人 print ("服務端啟動...") # conn,addr= sk.accept() # while True: # client_data=conn.recv(1024) # if str(client_data,"utf8")==‘exit‘: # break # print (str(client_data,"utf8")) # server_response=input(">>>") # conn.sendall(bytes(server_response,"utf8")) # conn.close() while True: conn,address = sk.accept() print(address) while True: try: client_data=conn.recv(1024) except: print("意外中斷") break print (str(client_data,"utf8")) server_response=input(">>>") conn.sendall(bytes(server_response,"utf8")) conn.close()
client
#__author: greg #date: 2017/9/16 16:11 import socket ip_port = (‘127.0.0.1‘,8888) sk = socket.socket() sk.connect(ip_port) print ("客戶端啟動:") # while True: # inp = input(‘>>>‘) # sk.sendall(bytes(inp,"utf8")) # if inp == ‘exit‘: # break # server_response=sk.recv(1024) # print (str(server_response,"utf8")) # sk.close() while True: inp=input(‘>>>‘) if inp=="exit": break sk.send(bytes(inp,‘utf8‘)) data=sk.recv(1024) print(str(data,"utf8")) sk.close()
一對多,簡單並發
server
#__author: greg #date: 2017/9/16 16:27 import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): print ("服務端啟動...") while True: conn = self.request print (self.client_address) while True: client_data=conn.recv(1024) print (str(client_data,"utf8")) print ("waiting...") conn.sendall(client_data) conn.close() if __name__ == ‘__main__‘: server = socketserver.ThreadingTCPServer((‘127.0.0.1‘,8091),MyServer) server.serve_forever()
client
#__author: greg #date: 2017/9/16 16:27 import socket ip_port = (‘127.0.0.1‘,8091) sk = socket.socket() sk.connect(ip_port) print ("客戶端啟動:") while True: inp = input(‘>>>‘) sk.sendall(bytes(inp,"utf8")) if inp == ‘exit‘: break server_response=sk.recv(1024) print (str(server_response,"utf8")) sk.close()
聊天並發實例
server
#__author: greg #date: 2017/9/16 20:55 import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): #handle父類有handle方法 print ("服務端啟動...") while True: conn = self.request print (self.client_address) while True: client_data=conn.recv(1024) print (str(client_data,"utf8")) print ("waiting...") server_response=input(">>>") conn.sendall(bytes(server_response,"utf8")) # conn.sendall(client_data) conn.close() # print self.request,self.client_address,self.server if __name__ == ‘__main__‘: server = socketserver.ThreadingTCPServer((‘127.0.0.1‘,8098),MyServer) server.serve_forever()
client
#__author: greg #date: 2017/9/16 20:54 import socket ip_port = (‘127.0.0.1‘,8098) sk = socket.socket() sk.connect(ip_port) print ("客戶端啟動:") while True: inp = input(‘>>>‘) sk.sendall(bytes(inp,"utf8")) server_response=sk.recv(1024) print (str(server_response,"utf8")) if inp == ‘exit‘: break sk.close()
下一篇:socketserver
Python網絡編程篇之socket