網絡編程python
Socket介紹linux
Socket類型編程
TCP編程***windows
TCP服務端服務器
#問題 #兩次綁定同一個監聽端口會怎樣?會報錯。 import socket s = socket.socket()#建立socket()對象 s.bind(('127.0.0.1',9999))#一個二元組 s.listen() #開啓一個鏈接 s1,info = s.accept()#阻塞直到和客戶端成功創建鏈接,返回一個socket對象和客戶端的地址 #使用緩衝區獲取數據 data = s1.recv(1024) print(data,info) s1.send(b'magedu.com ack') #開啓另外一個鏈接 s2,_ = s.accept() data = s2.recv(1024) s2.send(b'hello python') s.close()
練習網絡
# Author: Baozi #-*- codeing:utf-8 -*- import socket import threading import logging import datetime FORMAT="%(asctime)s %(threadName)s %(thread)d %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) #TCP Server class ChatServer(): def __init__(self,ip='10.39.27.10',port=9999): self.addr = (ip,port) self.socket = socket.socket() self.clients = {} self.event = threading.Event() def start(self): self.socket.bind(self.addr) self.socket.listen() # 服務啓動了 threading.Thread(target=self.accept,name='accept').start() def accept(self): while not self.event.is_set(): s,raddr = self.socket.accept()#阻塞 # f = s.makefile(mode='rw') self.clients[raddr] = s threading.Thread(target=self.recv,args=(s,raddr),name='recv').start() def recv(self,sock:socket.socket,client):#這裏是一對多模式 while not self.event.is_set(): try: # data = f.readline()#string data = sock.recv(1024)#阻塞,bytes logging.info(data) except Exception as e: logging.error(e) data = b'quit' if data == b'quit': self.clients.pop(client) sock.close() logging.info("{} quits".format(client)) break msg = "ack!!{}!!{}!!{}".format( client, datetime.datetime.now().strftime("%Y/%m/%d-%H:%M:%S"), data) for f in self.clients.values(): f.write(msg) f.flush() def stop(self): for s in self.clients.values(): s.close() self.event.set() self.socket.close() cs = ChatServer() cs.start() while True: cmd = input(">>>") if cmd == 'quit': cs.stop() threading.Event.wait(3) break logging.info(threading.enumerate())
其餘方法架構
#使用makefile import socket import threading socketserver = socket.socket() ip = '127.0.0.1' port = 9999 addr = (ip,port) socketserver.bind(addr) socketserver.listen() event = threading.Event() print('-'*30) def accept(socket:socket.socket,e:threading.Event): s,_ = socket.accept() f = s.makefile(mode='rw') while True: line = f.readline()#阻塞等 print(line) if line.strip() == "quit":#注意要發quit\n break f.write('Return your msg:{}'.format(line)) f.flush() f.close() socket.close() e.wait(3) t = threading.Thread(target=accept,args=(socketserver,event)) t.start() t.join() print(socketserver)
練習socket
import socket import threading import logging import datetime FORMAT="%(asctime)s %(threadName)s %(thread)d %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) #TCP Server class ChatServer(): def __init__(self,ip='127.0.0.1',port=9999): self.addr = (ip,port) self.socket = socket.socket() self.clients = {} self.event = threading.Event() def start(self): self.socket.bind(self.addr) self.socket.listen() # 服務啓動了 threading.Thread(target=self.accept,name='accept').start() def accept(self): while not self.event.is_set(): s,raddr = self.socket.accept()#阻塞 f = s.makefile(mode='rw') self.clients[raddr] = f threading.Thread(target=self.recv,args=(f,raddr),name='recv').start() def recv(self,f:socket.socket,client):#這裏是一對多模式 while not self.event.is_set(): try: data = f.readline()#string # data = s.recv(1024)#阻塞,bytes logging.info(data) except Exception as e: logging.error(e) data = 'quit' if data == 'quit': self.clients.pop(client) f.close() logging.info("{} quits".format(client)) break msg = "ack!!{}!!{}!!{}".format( client, datetime.datetime.now().strftime("%Y/%m/%d-%H:%M:%S"), data) for f in self.clients.values(): f.write(msg) f.flush() def stop(self): for f in self.clients.values(): f.close() self.event.set() self.socket.close() cs = ChatServer() cs.start() while True: cmd = input(">>>") if cmd == 'quit': cs.stop() threading.Event.wait(3) break logging.info(threading.enumerate())
TCP客戶端編程測試
客戶端編程步驟ui
import socket import threading import logging FORMAT="%(asctime)s %(threadName)s %(thread)s %(message)s" logging.basicConfig(format=FORMAT,level=logging.INFO) class ChatClient: def __init__(self,rip='127.0.0.1',rport=9999): self.raddr = (rip,rport) self.socket = socket.socket() self.event = threading.Event() def start(self): self.socket.connect(self.raddr)#創建鏈接 threading.Thread(target=self.recv,name='recv').start() def recv(self): while not self.event.is_set(): data = self.socket.recv(1024)#阻塞 logging.info(data) def send(self,msg:str): self.socket.send("{}\n".format(msg).encode()) def stop(self): self.socket.close() self.event.set() def main(): cc = ChatClient() cc.start() while True: cmd = input('>>>') if cmd.strip() == "quit": cc.stop() break cc.send(cmd) print(threading.enumerate()) if __name__ == '__main__': main()
UDP編程
測試命令
> netstat -anp udp | find "9988" #windows查找udp是否啓動端口 $ echo "123abc" | nc -u 127.0.0.1 9988 #linux下發給服務端數據
UDP服務端編程
UDP服務端編程流程
import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(('0.0.0.0',9999))#當即綁定一個udo端口 # data = server.recv(1024)#阻塞等待數據 data = server.recvfrom(1024)#阻塞等待數據(value,(ip,port)) print(data) server.sendto(b'ack',('10.39.27.2',10000)) server.close()
UDP客戶端編程流程
import socket client = socket.socket(type=socket.SOCK_DGRAM) raddr = ('10.39.27.2',9999) client.connect(raddr) client.sendto(b'8',raddr) client.send(b'9') data = client.recvfrom(1024)#阻塞等待數據(value,(ip,port)) print(data) data = client.recv(1024)#阻塞等待數據 print(data) client.close()
練習---UDP版羣聊
UDP羣聊服務端代碼
#服務端基本架構 class UDPChatserver: def __init__(self,ip='0.0.0.0',port=9999): self.addr = (ip,port) self.socket = socket.socket(type=socket.SOCK_DGRAM) def start(self): self.socket.bind(self.addr)#綁定本地的地址和端口 self.socket.recvfrom(1024)#阻塞接收數據 def stop(self): self.socket.close()
#在上面基礎上擴充 #UDP羣聊 import threading import logging import socket FORMAT="%(asctime)s %(threadName)s %(thread)d %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) class UDPChatserver: def __init__(self,ip='0.0.0.0',port=9999): self.addr = (ip,port) self.socket = socket.socket(type=socket.SOCK_DGRAM) self.clients = set()#記錄客戶端 self.event = threading.Event() def start(self): self.socket.bind(self.addr)#啓動並綁定本地的地址和端口 threading.Thread(target=self.recv,name='recv').start() def recv(self): while not self.event.is_set(): data,raddr = self.socket.recvfrom(1024)#阻塞 if data.strip() == b'quit': if raddr in self.clients:#有可能發來數據的客戶端不在clients中 self.clients.remove(raddr) logging.info('{} leaving'.format(raddr)) continue self.clients.add(raddr) msg = 'Ack. {}. from {}:{}'.format(data,*raddr).encode() for c in self.clients: self.socket.sendto(msg,c)#不保證對方能收到 def stop(self): for c in self.clients: self.socket.sendto(b'bye',c) self.socket.close() self.event.set() def main(): cs = UDPChatserver() cs.start() while True: cmd = input('>>>') if cmd.strip() == 'quit': cs.stop() break logging.info(threading.enumerate()) logging.info(cs.clients) if __name__ == '__main__': main()
#UDP羣聊客戶端代碼 import threading import logging import socket FORMAT="%(asctime)s %(threadName)s %(thread)d %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) class ChatUdpClient: def __init__(self,rip='127.0.0.1',rport=9999): self.socket = socket.socket(type=socket.SOCK_DGRAM) self.raddr = (rip,rport) self.event = threading.Event() def start(self): self.socket.connect(self.raddr)#佔用本地地址和端口,設置遠端地址和端口 threading.Thread(target=self.recv,name='recv').start() def recv(self): while not self.event.is_set(): data,raddr = self.socket.recvfrom(1024) msg = '{} .from {}:{}'.format(data.encode(),*raddr) logging.info(msg) def send(self,msg:str): self.socket.sendto(msg.encode(),self.raddr) def stop(self): self.socket.close() self.event.set() def main(): cc1 = ChatUdpClient() cc2 = ChatUdpClient() cc1.start() cc2.start() while True: cmd = input(">>>") if cmd.strip() == 'quit': cc1.stop() cc2.stop() break cc1.send(cmd) cc2.send(cmd) if __name__ == '__main__': main()
代碼改進
服務端代碼改進
#服務器端增長心跳機制 #心跳機制 import threading import logging import socket import datetime FORMAT="%(asctime)s %(threadName)s %(thread)d %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) class ChatUDPServer: def __init__(self,ip='127.0.0.1',port=9999,interval=10): self.addr = (ip,port) self.socket = socket.socket(type=socket.SOCK_DGRAM) self.event = threading.Event() self.clients = {}#記錄客戶端 self.interval = interval#默認10秒,超時就要移除對應的客戶端 def start(self): self.socket.bind(self.addr) threading.Thread(target=self.recv,name='recv').start() def recv(self): while not self.event.is_set(): localset = set()#清理超時 data,raddr = self.socket.recvfrom(1024)#阻塞接收數據 current = datetime.datetime.now().timestamp()#float if data.strip() == b'^hb^':#心跳信息 print("^^^hb^^^^",raddr) self.clients[raddr] = current continue elif data.strip() == b'quit': #有可能發來數據不在clients中 self.clients.pop(raddr,None) logging.info('{} .leaving'.format(raddr)) continue #有信息來就更新時間 #何時比較心跳時間呢?發送信息的時候,反正要遍歷一遍 self.clients[raddr] = current msg = "{} .from {}:{}".format(data.decode(),*raddr) logging.info(msg) msg = msg.encode() for c,stamp in self.clients.items(): if current-stamp>self.interval: localset.add(c) else: self.socket.sendto(msg,c)#不保證對方能收到 for c in localset: self.clients.pop(c) def stop(self): for c in self.clients: self.socket.sendto(b'bye',c) self.socket.close() self.event.set() def main(): cs = ChatUDPServer() cs.start() while True: cmd = input(">>>") if cmd.strip() == 'quit': cs.stop() break logging.info(threading.enumerate()) logging.info(cs.clients) if __name__ == '__main__': main()
#客戶端增長心跳機制 import threading import logging import socket FORMAT="%(asctime)s %(threadName)s %(thread)d %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT) class ChatUdpClient: def __init__(self,rip='127.0.0.1',rport=9999): self.socket=socket.socket(type=socket.SOCK_DGRAM) self.raddr = (rip,rport) self.event = threading.Event() def start(self): self.socket.connect(self.raddr)#佔用本地地址和端口,設置遠端地址和端口 threading.Thread(target=self._sendhb,name='heartbeat').start() threading.Thread() def _sendhb(self):#心跳 while not self.event.wait(5): self.socket.send(b'^hb^') def recv(self): while not self.event.is_set(): data,raddr = self.socket.recvfrom(1024) msg = '{}. from {}:{}'.format(data.encode(),*raddr) logging.info(msg) def send(self,msg:str): self.socket.sendto(msg.encode(),self.raddr) def stop(self): self.send('quit')#通知服務端退出 self.socket.close() self.event.set() def main(): cc1 = ChatUdpClient() cc2 = ChatUdpClient() cc1.start() cc2.start() print(cc1) print(cc2) while True: cmd = input(">>>") if cmd.strip() == 'quit': cc1.stop() cc2.stop() break cc1.send(cmd) cc2.send(cmd) if __name__ == '__main__': main()
UDP協議應用