• 网络编程---TCP/UDP编程


    网络编程

    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协议应用

    做一枚奔跑的老少年!
  • 相关阅读:
    Java单链表的实现方法汇总整理
    Java内存模型(JMM)图文并茂,条理清晰
    Java基础知识点整理(2021年最新版)
    Spring常见的77道面试题及答案(2021版)
    Python第二次学习知识点总结
    VUE学习十九,表单输入绑定v-model
    VUE学习十八,事件处理v-on
    VUE学习十七,列表渲染v-for
    VUE学习十五,Class与Style绑定
    VUE学习十四,侦听器
  • 原文地址:https://www.cnblogs.com/xiaoshayu520ly/p/10910841.html
Copyright © 2020-2023  润新知