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_INET6 | IPv6 |
AF_UNIX | UNIX 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