15 Socket模块 TCP/UDP编程

Socket

In [4]: socket?
Type:        module
String form: <module 'socket' from 'C:\\ProgramData\\Anaconda3\\lib\\socket.py'>
File:        c:\programdata\anaconda3\lib\socket.py
Docstring:
This module provides socket operations and some related functions.
On Unix, it supports IP (Internet Protocol) and Unix domain sockets.
On other systems, it only supports IP. Functions specific for a
socket are available as methods of the socket object.
  • Socket,中文名称:套接字
  • 简而言之,套接字就是IP地址:端口号. 如127.0.0.1:80
  • 一个端口号与操作系统内一个进程相对应

  • socket初始化 :

socket.socket(family=<AddressFamily.AF_INET: 2>, 
type=<SocketKind.SOCK_STREAM: 1>, 
proto=0, fileno=None)
  • AddressFamily选项 :
名称含义
AF_INET(默认值)IPv4
AF_INET6IPv6
AF_UNIXUNIX Domain Socket,Windows没有

- type选项 :

名称含义
SOCK_STREAM(默认值)面向连接的流套接字,TCP协议
SOCK_DGRAM无连接的数据报文套接字,UDP协议

- TCP连接示意图
- 注:建立TCP连接后会建立新的socket对象,数据收发是在新的socket上进行的


  • TCP Server端 :
TCP Server核心用法含义
socketobj=socket.socket()创建socket对象,默认为ipv4,TCP协议
socketobj.bind(‘127.0.0.1’,9999)绑定ip地址与端口
socketobj.listen()监听
new_socket_obj,raddr=socketobj.accept()(阻塞等待)建立TCP连接,生成新的socket对象与远程socket
data=new_socket_obj.recv(1024)(阻塞等待)接受远程主机发送的数据,数据类型为bytes
new_socket_obj.send(b’ack’)向远程主机发送bytes类型的数据
fileobj=new_socket_obj(mode=’rw’,buffer=1024)创建一个与该套接字相关的文件对象
fileobj.read(1024)读文件,类似new_socket_obj.recv(1024)
fileobj.write(1024)写文件,类似new_socket_obj.send(1024)
fileobj.flush()将缓冲区中的数据立刻写入文件,同时清空缓冲区
new_socket_obj.close()关闭socket
socketobj.close()关闭socket

- 简单实例

import socket

server=socket.socket()
server.bind(('127.0.0.1',9999))
server.listen()
s,i=server.accept()
while True:
    data=s.recv(1024)
    print(data.decode(encoding='cp936'))
    # print(data)
    s.send('ack {}'.format(data.decode(encoding='cp936')).encode(encoding='cp936'))
s.close()
server.close()
  • TCPServer端代码
import socket
import datetime
import threading
import logging
from threading import Thread,Event
import time


logging.basicConfig(level=logging.INFO,format='asctime:%(asctime)s thread:%(thread)d \n%(message)s')

class Server:
    def __init__(self,ip='127.0.0.1',port=9999):#初始化 定义ip与端口
        self.server=socket.socket()
        self.addr=(ip,port)
        self.clients={} #记录连接的客户端
        self.event=Event()
    def start(self):#启动监听
        self.server.bind(self.addr)#绑定
        self.server.listen()#监听
        Thread(target=self.accept).start()#accept方法会阻塞主线程,故新开线程
    def accept(self):#多人连接
        while not self.event.is_set():
            s,i=self.server.accept()
            f=s.makefile(mode='rw')#增加makefile
            self.clients[i]=f #记录连接的客户端
            Thread(target=self.recv,args=(f,i)).start()#recv方法会阻塞主线程,故新开线程
    def recv(self,s,i):
        while not self.event.is_set():
            try:
                data=s.readline()#阻塞到换行符
            except Exception as e:
                logging.error(e)
                data='quit'#有任何异常则退出
            if data.strip() == 'quit':
                self.clients.pop(i)
                s.close()
                logging.info('{} quit'.format(i))
                break
            msg='time:{:%Y%m%d %H:%M:%S}\nremote socket:{}:{}\ndata:{}\n'.format(datetime.datetime.now(),*i,data)
            logging.info(msg)
            # msg=msg.encode(encoding='gbk')
            for c in self.clients.values():
                # c.send(msg)
                c.write(msg)
                c.flush()
    def stop(self):#停止服务:关闭已建立的连接
        print(1)
        for c in self.clients.values():
            c.close()
        print(2)
        self.server.close()
        print(3)
        self.event.set()
        print(4)

def main():
    cs=Server()
    cs.start()
    while True:
        cmd=input('>>>').strip()
        if cmd == 'quit':
            # print(' qwertyui')
            cs.stop()
            print('The Server is shuting down in 3 seconds')
            Event().wait(3)
            break #停止服务器主进程
        logging.info(threading.enumerate())

if __name__ == '__main__':
    main()


  • TCP Client端 :
TCP Client核心用法含义
client=socket.socket()创建socket对象,默认为ipv4,TCP协议
client.connect(‘127.0.0.1’,99990)连接服务器
clent.send(b’asdf’)向服务器发送数据
client.recv(1024)阻塞等待
client.close()关闭socket

- TCPClient端代码 :

import socket
import datetime
import threading
import logging
from threading import Thread,Event


logging.basicConfig(level=logging.INFO,format='asctime:%(asctime)s thread:%(thread)d \n%(message)s')

class Client:
    def __init__(self,ip='127.0.0.1',port=9999):
        self.client=socket.socket()
        self.addr=(ip,port)
        self.event=Event()
    def start(self):
        self.client.connect(self.addr)
        self.send('Ready')
        Thread(target=self.recv).start()
    def recv(self):
        while not self.event.is_set():
            try:
                data=self.client.recv(1024)
            except Exception as e:
                logging.error(e)
                break
            msg='time:{:%Y%m%d %H:%M:%S}\nremote socket:{}:{}\ndata:{}\n'.format(datetime.datetime.now(),*self.addr,data)
            logging.info(msg)
    def send(self,msg:str):
        data='{}\n'.format(msg.strip()).encode(encoding='gbk')
        self.client.send(data)
    def stop(self):
        self.client.close()
        self.event.wait(3)
        self.event.set()
        logging.info('Client stops')


def main():
    cs=Client()
    cs.start()
    while True:
        cmd=input('>>>').strip()
        if cmd == 'quit':
            cs.stop()
            break 
        cs.send(cmd)

if __name__ == '__main__':
    main()

UDP

  • TCP连接示意图
  • 注:UDP是无连接协议

  • UDP Server端
UDP Server核心用法含义
socketobj=socket.socket(type=socket.SOCK_DGRAM)创建socket对象,UDP协议
socketobj.bind(‘127.0.0.1’,9999)绑定ip地址与端口
data,raddr=ocketobj.recvfrom(1024)(阻塞等待)接受远程主机发送的数据,数据类型为bytes. raddr为类似’127.0.0.1’,9999的格式
socketobj.sendto(b’ack’,raddr)向远程主机发送bytes类型的数据
socketobj.close()关闭socket

- UDP Server端代码

import socket
import datetime
import threading
import logging
from threading import Thread,Event

logging.basicConfig(level=logging.INFO,format='asctime:%(asctime)s thread:%(thread)d \n%(message)s')


class Server:
    def __init__(self,ip='127.0.0.1',port=9999,interval=10):
        self.addr=ip,port
        self.server=socket.socket(type=socket.SOCK_DGRAM)
        self.clients={}
        self.event=Event()
        self.interval=interval

    def start(self):
        self.server.bind(self.addr)
        Thread(target=self.recv).start()

    def recv(self):
        while not self.event.is_set():
            localset=set()
            data,raddr=self.server.recvfrom(1024)
            current=datetime.datetime.now().timestamp()
            if data.strip() == b'^hb^': #发送心跳包并记录时间
                self.clients[raddr]=current
                continue
            elif data.strip() == b'quit':
                self.clients.pop(raddr,None)
                logging.info('{} leaving'.format(raddr))
                continue
            self.clients[raddr]=current
            msg='{}. from {}:{}'.format(data.decode(encoding='gbk'),*raddr)
            logging.info(msg)
            msg=msg.encode(encoding='gbk')
            for c,stamp in self.clients.items():
                if current - stamp > self.interval:
                    localset.add(c) #记录超时的主机
                else:
                    self.server.sendto(msg,c)
                for c in localset:
                    self.clients.pop(c)#在字典中去除超时的主机
    def stop(self):
        for c in self.clients:
            self.server.sendto(b'bye',c)
        self.server.close()
        self.event.set()
def main():
    cs=Server()
    cs.start()
    while True:
        if input('>>>').strip() == 'quit':
            cs.stop()
            break
        logging.info(threading.enumerate())
        logging.info(cs.clients)

if __name__ == '__main__':
    main()
  • UDP Client端
UDP Client核心用法含义
client=socket.socket(type=socket.SOCK_DGRAM)创建socket对象,UDP协议
client.sendto(b’OK’,’127.0.0.1’,9999)向远程主,机发送bytes类型的数据
data,raddr=client.recvfrom(1024)(阻塞等待)接受远程主机发送的数据,数据类型为bytes.
client.close()关闭套接字

- UDP Client端代码

class Client:
    def __init__(self,ip='127.0.0.1',port=9999,interval=10):
        self.addr=ip,port
        self.client=socket.socket(type=socket.SOCK_DGRAM)
        self.event=Event()

    def start(self):
        self.client.connect(self.addr)
        Thread(target=self._sendhb,daemon=True).start()
        Thread(target=self.recv).start()

    def _sendhb(self):
        while  not self.event.is_set():
            self.client.send(b'^hb^')
    def recv(self):
        while not self.event.is_set():
            localset=set()
            data,raddr=self.client.recvfrom(1024)
            msg='{}. from {}:{}'.format(data.decode(encoding='gbk'),*raddr)
            logging.info(msg)
    def send(self,msg:str):
        self.client.sendto(msg.encode(encoding='gbk'),self.addr)
    def stop(self):
        self.client.send('quit')
        self.client.close()
        self.event.set()
def main():
    cs1=Server()
    cs2=Server()
    cs1.start()
    cs1.start()
    while True:
        if input('>>>').strip() == 'quit':
            cs1.stop()
            cs2.stop()
            break
        cs1.send(cmd)
        cs2.send(cmd)

if __name__ == '__main__':
    main()
UDP特点与应用场景
  • UDP是无连接协议,适合网络足够好 消息不会丢包 包不会乱序的场景
  • 应用场景 : 音频/视频传输 DNS查询
  • 一般而言,UDP性能高于TCP.
  • 但对可靠性要求高的场合还要选择TCP
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值