当前位置:   article > 正文

python3套接字编程之socket和socketserver(TCP和UDP通信)_python socketserver

python socketserver

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

5.额外补充:strace分析Python中subprocess.Popen实现

5.1错误命令

5.2正确命令


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

  1. # -*- coding: UTF-8 -*-
  2. import socket
  3. from socket import SOL_SOCKET, SO_REUSEADDR
  4. import subprocess
  5. import struct
  6. import json
  7. PORT = 18284
  8. #简单TCP通信
  9. def main():
  10. tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  11. print(tcpSocket)
  12. tcpSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
  13. tcpSocket.bind(('127.0.0.1', PORT))
  14. tcpSocket.listen(5)
  15. print('start....')
  16. while True:
  17. conn, client_addr = tcpSocket.accept()
  18. print('new client connected ', conn, client_addr)
  19. while True:
  20. try:
  21. print('recv data ...')
  22. data = conn.recv(1024)
  23. if len(data) == 0:
  24. break
  25. print('recv data is ', data)
  26. conn.send(data.upper())
  27. except ConnectionResetError:
  28. break
  29. conn.close()
  30. phone.close()
  31. #仿写ssh服务程序
  32. def main1():
  33. server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  34. server.bind(('127.0.0.1', PORT))
  35. server.listen(5)
  36. print('start...')
  37. while True:
  38. conn, client_addr = server.accept()
  39. while True:
  40. print('from client:', client_addr)
  41. cmd = conn.recv(1024)
  42. if len(cmd) == 0:
  43. break
  44. print('cmd:', cmd)
  45. obj = subprocess.Popen(cmd.decode('utf8'), # 输入的cmd命令
  46. shell=True, # 通过shell运行
  47. stderr=subprocess.PIPE, # 把错误输出放入管道,以便打印
  48. stdout=subprocess.PIPE) # 把正确输出放入管道,以便打印
  49. stdout = obj.stdout.read() # 打印正确输出
  50. stderr = obj.stderr.read() # 打印错误输出
  51. conn.send(stdout)
  52. conn.send(stderr)
  53. conn.close()
  54. server.close()
  55. #自定义数据包
  56. def main2():
  57. server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  58. server.bind(('127.0.0.1', PORT))
  59. server.listen(5)
  60. print('start...')
  61. while True:
  62. conn, client_addr = server.accept()
  63. print(conn, client_addr)
  64. while True:
  65. cmd = conn.recv(1024)
  66. obj = subprocess.Popen(cmd.decode('utf8'),
  67. shell=True,
  68. stderr=subprocess.PIPE,
  69. stdout=subprocess.PIPE)
  70. stderr = obj.stderr.read()
  71. stdout = obj.stdout.read()
  72. print("stderr:", stderr)
  73. print("stdout:", stdout)
  74. data_dict = {
  75. 'body_size': len(stdout) + len(stderr),
  76. 'body': stderr.decode('utf-8') + stdout.decode('utf-8')
  77. }
  78. data_json = json.dumps(data_dict)
  79. data_bytes = data_json.encode('utf8')
  80. conn.send(struct.pack('i', len(data_bytes)))
  81. conn.send(data_bytes)
  82. conn.close()
  83. break
  84. server.close()
  85. if __name__ == '__main__':
  86. main2()

client.py

  1. # -*- coding: UTF-8 -*-
  2. import socket
  3. import json
  4. import struct
  5. PORT = 18284
  6. def main():
  7. cs = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  8. cs.connect(('127.0.0.1', PORT))
  9. while True:
  10. msg = input('input data >>').strip()
  11. if len(msg) == 0:
  12. continue
  13. cs.send(msg.encode('utf-8'))
  14. data = cs.recv(1024)
  15. print(data)
  16. phone.close()
  17. def main2():
  18. client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  19. client.connect(('127.0.0.1', PORT))
  20. while True:
  21. cmd = input('enter cmd >> ')
  22. if len(cmd) == 0:
  23. continue
  24. #encode:字符串转字节数组
  25. client.send(cmd.encode('utf8'))
  26. data_len = struct.unpack('i', client.recv(4))[0]
  27. print("data_len: ", data_len)
  28. data_bytes = client.recv(data_len)
  29. print("data_bytes: ", data_bytes)
  30. #decode:字节数组转转字符串
  31. data_json = data_bytes.decode('utf8')
  32. print("data_json: ", data_json)
  33. data_dict = json.loads(data_json)
  34. print("data_dict: ", data_dict['body'])
  35. break
  36. client.close()
  37. if __name__ == '__main__':
  38. main2()

运行效果如下:

3.2 socketserver类型TCP

server.py

  1. # -*- coding: UTF-8 -*-
  2. import socketserver
  3. PORT = 18286
  4. class MyHandler(socketserver.BaseRequestHandler):
  5. def handle(self):
  6. while True:
  7. print(self.client_address)
  8. print(self.request)
  9. try:
  10. data = self.request.recv(1024)
  11. if len(data) == 0: break
  12. self.request.send(data.upper())
  13. except ConnectionResetError:
  14. break
  15. if __name__ == '__main__':
  16. s = socketserver.ThreadingTCPServer(('127.0.0.1', PORT),
  17. MyHandler,
  18. bind_and_activate=True)
  19. s.serve_forever()

client.py

  1. # -*- coding: UTF-8 -*-
  2. import socket
  3. PORT = 18286
  4. def main():
  5. client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  6. client.connect(('127.0.0.1', PORT))
  7. while True:
  8. msg=input('input msg >> ').strip()
  9. if len(msg) == 0: continue
  10. client.send(msg.encode('utf-8'))
  11. data = client.recv(1024)
  12. print(data.decode('utf-8'))
  13. client.close()
  14. if __name__ == '__main__':
  15. main()

4.UDP

关于UDP,有如下需要注意:
(1)无连接的,先启动哪一端都不会报错,并且可以同时多个客户端去跟服务端通信
(2)数据报协议,发空的时候也会自带报头,因此客户端输入空,服务端也能收到,一般用于传输小数据
(4)无粘包问题,但是不能替代TCP套接字,因为UPD协议有一个缺陷:如果数据发送的途中,数据丢失,则数据就丢失了,而TCP协议则不会有这种缺陷,因此UPD套接字多用于无关紧要的数据发送,例如IM聊天工具

3.1 socket类型UDP

server.py

  1. # -*- coding: UTF-8 -*-
  2. import socket
  3. PORT = 18285
  4. def main():
  5. server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  6. server.bind(('127.0.0.1', PORT))
  7. while True:
  8. data, client_addr = server.recvfrom(1024)
  9. print('recvfrom:', data, client_addr)
  10. server.sendto(data.upper(), client_addr)
  11. server.close()
  12. if __name__ == '__main__':
  13. main()

client.py

  1. # -*- coding: UTF-8 -*-
  2. import socket
  3. PORT = 18285
  4. def main():
  5. client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  6. while True:
  7. msg = input('input >> ').strip()
  8. client.sendto(msg.encode('utf-8'), ('127.0.0.1', PORT))
  9. data, server_addr = client.recvfrom(1024)
  10. print(data, data.decode('utf-8'))
  11. client.close()
  12. if __name__ == '__main__':
  13. main()


3.2 socketserver类型UDP

基于udp的socketserver自己定义的类,其中
self.request是一个元组(第一个元素是客户端发来的数据,第二部分是服务端的udp套接字对象)
self.client_address即客户端地址

server.py

  1. # -*- coding: UTF-8 -*-
  2. import socketserver
  3. PORT = 18287
  4. class MyHandler(socketserver.BaseRequestHandler):
  5. def handle(self):
  6. print(self.client_address)
  7. print(self.request)
  8. data = self.request[0]
  9. print('client msg:', data)
  10. self.request[1].sendto(data.upper(), self.client_address)
  11. if __name__ == '__main__':
  12. s = socketserver.ThreadingUDPServer(('127.0.0.1', PORT), MyHandler)
  13. s.serve_forever()

client.py

  1. # -*- coding: UTF-8 -*-
  2. import socket
  3. PORT = 18287
  4. def main():
  5. client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  6. while True:
  7. msg=input('input >> ').strip()
  8. client.sendto(msg.encode('utf-8'), ('127.0.0.1', PORT))
  9. data, server_addr = client.recvfrom(1024)
  10. print(data)
  11. client.close()
  12. if __name__ == '__main__':
  13. main()

运行效果如下:

 

5.额外补充:strace分析Python中subprocess.Popen实现

5.1错误命令

服务端:strace  -ff -o ./output  python3 server.py 

客户端:python3 client.py 

运行结果:

产生了两个文件,说明subprocess.Popen是通过多进程实现的。

主进程:

子进程:

分析:主进程pipe2 [5, 6],pipe2 [7, 8],创建两个匿名管道,用来接收子进程正确输出和错误输出,主进程用5,7读取正确和错误信息,子进程6,8来写正确和错误信息。调用clone产生子进程, dup2(6, 1) dup2(8, 2),使用1,2来代替6,8。子进程调用execve来覆盖子进程进程空间并在其中执行命令。

5.2正确命令

服务端:strace  -ff -o ./output  python3 server.py 

 客户端:python3 client.py 

 运行结果:

通过输出可以看到执行一次命令,会产生两个子进程。

主进程:

  子进程:

 孙子进程: 

 主进程14912调用clone产生子进程14986,子进程中通过execve执行sh程序来替换子进程空间, sh进程执行时又调用clone产生子进程14987,然后孙子进程14987调用execve来真正执行ls -i命令。 主进程 -> sh子进程 -> ls -i孙子进程

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/585068
推荐阅读
相关标签
  

闽ICP备14008679号