网络编程---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\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)

 

练习

 

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("{}\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协议应用

 

 

 

posted @ 2019-05-23 11:22  小鲨鱼~  阅读(272)  评论(0编辑  收藏  举报