端到端編程,創建了一條數據傳輸的通道編程
網絡編程的基礎,全部的操做系統都支持socketwindows
socket是一種通用的網路編程接口,不須要關心協議層,只是打通了一條通道,和網絡層次沒有一一對應關係。服務器
IPC,RPC,最底層也是socket實現網絡
名稱 | 含義 |
AF_INET | IPV4 |
AF_INET6 | IPV6 |
AF_UNIX | Unix Domain Socket,windows沒有 |
名稱 | 含義 |
SOCK_STREAM | 面向鏈接的流套接字,默認值,TCP協議 |
SOCK_DGRAM | 無鏈接的數據報文套接字,UDP協議,效率高,易產生亂序,丟包問題 |
服務端server,客戶端client,cs編程,socket
服務器編程步驟tcp
一、建立Socket對象ide
二、綁定IP地址和端口,IP負責地址,端口管應用程序性能
端口是進程的,不是線程的,只是線程共享進程的資源ui
三、監聽,暴露端口,將指定的IP端口開始監聽spa
四、socket accept 獲取傳送數據的socket對象
用於阻塞等待客戶端創建鏈接,返回一個新的socket對象和客戶端地址的二元組
地址是遠程客戶端地址,IPv4中是一個二元組(clientaddr,port)
recv(bufsize[,flags]) 使用緩衝區接收數據
send(bytes) 發送數據
注意:兩次綁定同一個端口會報錯,操做系統級別的錯誤
Windows報錯: 一般每一個套接字地址(協議/網絡地址/端口)只容許使用一次。
#1 socket sevrer = socket.socket() #2 bind ip = '127.0.0.1' port = 9999 addr = (ip,port) server.bind(addr) #3 listen server.listen() #4 accept s1, info = server.accept() #阻塞等待client鏈接 #5 recv,send data = server.recv(1024) s1.send("hello, I'm server") #6 close server.close()
netstat Windows使用 b 顯示進程,要用管理員權限 windows下
ss -tanl l表示監聽,t tcp協議,a所有,n數字顯示端口 Linux下
windows下的管道也可使用,findstr是過濾方法
羣聊程序TCP協議服務器端簡單實現
1 import socket 2 import threading 3 import logging 4 5 FORMAT = "%(thread)d %(threadName)s %(asctime)s %(message)s" 6 logging.basicConfig(format=FORMAT,level=logging.INFO) 7 8 class ChatServer: 9 def __init__(self,ip="127.0.0.1",port=9999): 10 self.sock = socket.socket() 11 self.addr = (ip,port) 12 self.clients = {} 13 self.event = threading.Event() 14 15 def start(self):#開始監聽 16 self.sock.bind(self.addr) 17 self.sock.listen() 18 #accept會阻塞主線程,開線程跑 19 threading.Thread(target=self.accept,name="accept").start() 20 21 def accept(self):#多人鏈接 22 while not self.event.is_set(): 23 try: 24 sock,raddr = self.sock.accept()#阻塞等待 25 except Exception: 26 self.stop() 27 break 28 self.clients[raddr] = sock#記錄客戶端 29 logging.info(raddr) 30 #開啓新線程,一個新的socket對象處理單個client 31 threading.Thread(target=self.recv,args=(sock,raddr),name="r-{}".format(raddr)).start() 32 33 34 def recv(self,sock,raddr):#接受數據 35 while not self.event.is_set(): 36 try: 37 data = sock.recv(1024).strip()#阻塞等待 38 except Exception: 39 break 40 logging.info(":{}".format(data.decode())) 41 #client退出機制 42 if data.strip() == b"quit" or data ==b"": 43 print(data,"~~~~~~~~~~") 44 self.clients.pop(raddr) 45 sock.close() 46 break 47 48 msg = "{}".format(data) 49 msg = msg.encode() 50 for client in self.clients.values(): 51 client.sendall(msg) 52 53 def stop(self):#中止服務 54 for c in self.clients.values(): 55 c.close() 56 self.sock.close() 57 self.event.set() 58 59 def main(): 60 server = ChatServer() 61 server.start() 62 63 while True: 64 cmd = input(">>>") 65 if cmd == "quit": 66 server.stop() 67 threading.Event().wait(3) 68 break 69 logging.info(threading.enumerate())#觀察線程變化 70 71 if __name__ == '__main__': 72 main()
只是實現主要功能,且如今不用該方法了
名稱 | 含義 |
socket.recv(bufsize[, flags]) | 獲取數據。默認是阻塞的方式 |
socket.recvfrom(bufsize[, flags]) | 獲取數據,返回一個二元組(bytes, address) |
socket.recv_into(buffer[, nbytes[,flags]]) | 獲取到nbytes的數據後,存儲到buffer中。若是nbytes沒有指定或0,將buffer大小的數據存入buffer中。返回接收的字節數。 |
socket.recvfrom_into(buffer[,nbytes[, flags]]) | 獲取數據,返回一個二元組(bytes, address)到buffer中 |
socket.send(bytes[, flags]) | TCP發送數據 |
socket.sendall(bytes[, flags]) | TCP發送所有數據,成功返回None |
socket.sendto(string[,flag],address) | UDP發送數據 |
socket.sendfile(file, offset=0,count=None) | 發送一個文件直到EOF,使用高性能的os.sendfile機制,返回發送的字節數。若是win下不支持sendfile,或者不是普通文件,使用send()發送文件。offset告訴起始位置。3.5版本開始 |
socket.getpeername() | 返回鏈接套接字的遠程地址。返回值一般是元組(ipaddr,port) |
socket.getsockname() | 返回套接字本身的地址。一般是一個元組(ipaddr,port) |
socket.setblocking(flag) | 若是flag爲0,則將套接字設爲非阻塞模式,不然將套接字設爲阻塞模式(默認值)非阻塞模式下,若是調用recv()沒有發現任何數據,或send()調用沒法當即發送數據,那麼將引發socket.error異常 |
socket.settimeout(value) | 設置套接字操做的超時期,timeout是一個浮點數,單位是秒。值爲None表示沒有超時期。通常,超時期應該在剛建立套接字時設置,由於它們可能用於鏈接的操做(如connect()) |
socket.setsockopt(level,optname,value) | 設置套接字選項的值。好比緩衝區大小。太多了,去看文檔。不一樣系統,不一樣版本都不盡相同 |
import socket import threading import logging FORMAT = "%(asctime)s %(thread)d %(message)s" logging.basicConfig(format=FORMAT,level=logging.INFO) class ChatTCPServer: def __init__(self,ip="127.0.0.1",port=9999): self.sock = socket.socket() self.addr = ip,port self.event = threading.Event() self.clients = {} def start(self): self.sock.bind(self.addr) self.sock.listen() threading.Thread(target=self.accept,name="accept").start() def accept(self): while not self.event.is_set(): try: s, client = self.sock.accept() f = s.makefile("rw") except: self.stop() break self.clients[client] = f threading.Thread(target=self.recv,args=(f, client,s),name="client-{}".format(client[1])).start() logging.info("{} login".format(client)) def recv(self,f,raddr,s): while not self.event.is_set(): try: msg = f.readline().strip() except Exception as e: logging.error(e) self.clients.pop(raddr) f.close() break logging.info(msg) if msg == "quit" or "": self.clients.pop(raddr) f.close() break for f1 in self.clients.values(): f1.write(msg) f1.flush() def stop(self): for client in self.clients.values(): client.close() self.sock.close() self.event.set() def main(): server = ChatTCPServer() server.start() while True: cmd = input(">>>") if cmd =="quit": server.stop() threading.Event().wait(3) break logging.info(threading.enumerate()) if __name__ == '__main__': main()
建立socket對象
鏈接到遠程服務端的ip,port,connect方法
傳輸數據
使用send,recv方法接受,發送數據
關閉鏈接,釋放資源
1 import socket 2 import threading 3 import logging 4 import datetime 5 6 logging.basicConfig(format="%(asctime)s %(thread)s %(message)s",level=logging.INFO) 7 8 class ChatTcpClient: 9 def __init__(self,ip="127.0.0.1",port=9999): 10 self.sock = socket.socket() 11 self.event = threading.Event() 12 self.raddr = ip,port 13 14 def start(self): 15 self.sock.connect(self.raddr) 16 self.sock.send(b"I'm ready") 17 threading.Thread(target=self.recv,name='recv').start() 18 19 def recv(self): 20 while not self.event.is_set(): 21 try: 22 data = self.sock.recv(1024).decode() 23 except Exception as e: 24 logging.info(e) 25 break 26 msg = "{:%Y%m%d %H:%M:%S} {}:{}\n{}".format(datetime.datetime.now(),*self.raddr,data.strip()) 27 logging.info(msg) 28 29 def send(self,msg:str): 30 data = "{}\n".format(msg.strip()).encode() 31 self.sock.send(data) 32 33 def stop(self): 34 self.sock.close() 35 self.event.wait(3) 36 self.event.set() 37 logging.info("Client stop") 38 39 def main(): 40 cc = ChatTcpClient() 41 cc.start() 42 while True: 43 cmd = input(">>>") 44 if cmd == "quit" or "": 45 cc.stop() 46 break 47 cc.send(cmd) 48 49 if __name__ == '__main__': 50 main()