socket和socketserver是python3中socket通信模块,关于其使用做如下总结。
目录
1.socket
1.1模块引入
1.2套接字获取
1.3套接字接口
1.3.1 服务端
1.3.2 客户端套接字函数
1.3.3 公共套接字函数
1.3.4 面向锁的套接字方法
1.3.5 面向文件的套接字的函数
2.socketserver
3.TCP
3.1 socket类型TCP
3.2 socketserver类型TCP
4.UDP
3.1 socket类型UDP
3.2 socketserver类型UDP
1.socket
1.1模块引入
import socket
1.2套接字获取
接口:socket.socket(socket_family, socket_type, protocal=0)
参数:
socket_family:AF_UNIX 或 AF_INET
socket_type:SOCK_STREAM 或 SOCK_DGRAM
protocol: 一般不填,默认值为 0
(1)tcp套接字
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
(2)udp套接字
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
1.3套接字接口
1.3.1 服务端
s.bind():绑定(主机,端口号)到套接字
s.listen():TCP监听
s.accept():接受TCP客户的连接
1.3.2 客户端套接字函数
s.connect():初始化TCP服务器连接
s.connect()
s.connect_ex():函数的扩展版本,出错时返回出错码,而不是抛出异常
1.3.3 公共套接字函数
s.recv():接收TCP数据
s.send():发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall():发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom():接收UDP数据
s.sendto():发送UDP数据
s.getpeername():连接到当前套接字的远端的地址
s.getsockname():当前套接字的地址
s.getsockopt():返回指定套接字的参数
s.setsockopt():设置指定套接字的参数
s.close():关闭套接字
1.3.4 面向锁的套接字方法
s.setblocking():设置套接字的阻塞与非阻塞模式
s.settimeout():设置阻塞套接字操作的超时时间
s.gettimeout():得到阻塞套接字操作的超时时间
1.3.5 面向文件的套接字的函数
s.fileno():套接字的文件描述符
s.makefile():创建一个与该套接字相关的文件
2.socketserver
socketserver是socket的升级版本,可以并发处理多个客户端的连接,其包含两个大类server类和request类,server类解决连接问题,request类解决通信问题
引用如下:
import socketserver
3.TCP
3.1 socket类型TCP
server.py
# -*- coding: UTF-8 -*-
import socket
from socket import SOL_SOCKET, SO_REUSEADDR
import subprocess
import struct
import json
PORT = 18284
#简单TCP通信
def main():
tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(tcpSocket)
tcpSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
tcpSocket.bind(('127.0.0.1', PORT))
tcpSocket.listen(5)
print('start....')
while True:
conn, client_addr = tcpSocket.accept()
print('new client connected ', conn, client_addr)
while True:
try:
print('recv data ...')
data = conn.recv(1024)
if len(data) == 0:
break
print('recv data is ', data)
conn.send(data.upper())
except ConnectionResetError:
break
conn.close()
phone.close()
#仿写ssh服务程序
def main1():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', PORT))
server.listen(5)
print('start...')
while True:
conn, client_addr = server.accept()
while True:
print('from client:', client_addr)
cmd = conn.recv(1024)
if len(cmd) == 0:
break
print('cmd:', cmd)
obj = subprocess.Popen(cmd.decode('utf8'), # 输入的cmd命令
shell=True, # 通过shell运行
stderr=subprocess.PIPE, # 把错误输出放入管道,以便打印
stdout=subprocess.PIPE) # 把正确输出放入管道,以便打印
stdout = obj.stdout.read() # 打印正确输出
stderr = obj.stderr.read() # 打印错误输出
conn.send(stdout)
conn.send(stderr)
conn.close()
server.close()
#自定义数据包
def main2():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', PORT))
server.listen(5)
print('start...')
while True:
conn, client_addr = server.accept()
print(conn, client_addr)
while True:
cmd = conn.recv(1024)
obj = subprocess.Popen(cmd.decode('utf8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
stderr = obj.stderr.read()
stdout = obj.stdout.read()
print("stderr:", stderr)
print("stdout:", stdout)
data_dict = {
'body_size': len(stdout) + len(stderr),
'body': stderr.decode('utf-8') + stdout.decode('utf-8')
}
data_json = json.dumps(data_dict)
data_bytes = data_json.encode('utf8')
conn.send(struct.pack('i', len(data_bytes)))
conn.send(data_bytes)
conn.close()
break
server.close()
if __name__ == '__main__':
main2()
client.py
# -*- coding: UTF-8 -*-
import socket
import json
import struct
PORT = 18284
def main():
cs = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
cs.connect(('127.0.0.1', PORT))
while True:
msg = input('input data >>').strip()
if len(msg) == 0:
continue
cs.send(msg.encode('utf-8'))
data = cs.recv(1024)
print(data)
phone.close()
def main2():
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', PORT))
while True:
cmd = input('enter cmd >> ')
if len(cmd) == 0:
continue
#encode:字符串转字节数组
client.send(cmd.encode('utf8'))
data_len = struct.unpack('i', client.recv(4))[0]
print("data_len: ", data_len)
data_bytes = client.recv(data_len)
print("data_bytes: ", data_bytes)
#decode:字节数组转转字符串
data_json = data_bytes.decode('utf8')
print("data_json: ", data_json)
data_dict = json.loads(data_json)
print("data_dict: ", data_dict['body'])
break
client.close()
if __name__ == '__main__':
main2()
运行效果如下:
3.2 socketserver类型TCP
server.py
# -*- coding: UTF-8 -*-
import socketserver
PORT = 18286
class MyHandler(socketserver.BaseRequestHandler):
def handle(self):
while True:
print(self.client_address)
print(self.request)
try:
data = self.request.recv(1024)
if len(data) == 0: break
self.request.send(data.upper())
except ConnectionResetError:
break
if __name__ == '__main__':
s = socketserver.ThreadingTCPServer(('127.0.0.1', PORT),
MyHandler,
bind_and_activate=True)
s.serve_forever()
client.py
# -*- coding: UTF-8 -*-
import socket
PORT = 18286
def main():
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', PORT))
while True:
msg=input('input msg >> ').strip()
if len(msg) == 0: continue
client.send(msg.encode('utf-8'))
data = client.recv(1024)
print(data.decode('utf-8'))
client.close()
if __name__ == '__main__':
main()
4.UDP
关于UDP,有如下需要注意:
(1)无连接的,先启动哪一端都不会报错,并且可以同时多个客户端去跟服务端通信
(2)数据报协议,发空的时候也会自带报头,因此客户端输入空,服务端也能收到,一般用于传输小数据
(4)无粘包问题,但是不能替代TCP套接字,因为UPD协议有一个缺陷:如果数据发送的途中,数据丢失,则数据就丢失了,而TCP协议则不会有这种缺陷,因此UPD套接字多用于无关紧要的数据发送,例如IM聊天工具
3.1 socket类型UDP
server.py
# -*- coding: UTF-8 -*-
import socket
PORT = 18285
def main():
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(('127.0.0.1', PORT))
while True:
data, client_addr = server.recvfrom(1024)
print('recvfrom:', data, client_addr)
server.sendto(data.upper(), client_addr)
server.close()
if __name__ == '__main__':
main()
client.py
# -*- coding: UTF-8 -*-
import socket
PORT = 18285
def main():
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
msg = input('input >> ').strip()
client.sendto(msg.encode('utf-8'), ('127.0.0.1', PORT))
data, server_addr = client.recvfrom(1024)
print(data, data.decode('utf-8'))
client.close()
if __name__ == '__main__':
main()
3.2 socketserver类型UDP
基于udp的socketserver自己定义的类,其中
self.request是一个元组(第一个元素是客户端发来的数据,第二部分是服务端的udp套接字对象)
self.client_address即客户端地址
server.py
# -*- coding: UTF-8 -*-
import socketserver
PORT = 18287
class MyHandler(socketserver.BaseRequestHandler):
def handle(self):
print(self.client_address)
print(self.request)
data = self.request[0]
print('client msg:', data)
self.request[1].sendto(data.upper(), self.client_address)
if __name__ == '__main__':
s = socketserver.ThreadingUDPServer(('127.0.0.1', PORT), MyHandler)
s.serve_forever()
client.py
# -*- coding: UTF-8 -*-
import socket
PORT = 18287
def main():
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
msg=input('input >> ').strip()
client.sendto(msg.encode('utf-8'), ('127.0.0.1', PORT))
data, server_addr = client.recvfrom(1024)
print(data)
client.close()
if __name__ == '__main__':
main()
运行效果如下: