网络编程
Socket介绍
Socket类型
TCP编程***
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 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)
练习
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客户端编程
客户端编程步骤
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("{} ".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协议应用