socket
Socket 是网络编程的一个抽象概念. 通常我们用一个 Socket 表示 "打开了一个网络链接", 而打开一个 Socket 需要知道目标计算机的 IP 地址和端口号, 再指定协议类型即可.
socket 参数
socket 地址族
socket.``AF_UNIX unix 本机进程间通信
- socket.``AF_INET ipv4
- socket.``AF_INET6 ipv6
socket 类型
- socket.``SOCK_STREAM TCP
- socket.``SOCK_DGRAM UDP
- socket.``SOCK_RAW #原始套接字, 普通的套接字无法处理 ICMP,IGMP 等网络报文, 而 SOCK_RAW 可以; 其次, SOCK_RAW 也可以处理特殊的 IPv4 报文; 此外, 利用原始套接字, 可以通过 IP_HDRINCL 套接字选项由用户构造 IP 头.
- socket.``SOCK_RDM #是一种可靠的 UDP 形式, 即保证交付数据报但不保证顺序. SOCK_RAM 用来提供对原始协议的低级访问, 在需要执行某些特殊操作时使用, 如发送 ICMP 报文. SOCK_RAM 通常仅限于高级用户或管理员运行的程序使用.
- socket.``SOCK_SEQPACKET #不使用了
socket 方法
- socket.socket`(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
- socket.``accept()?
- socket.``bind(address)?
- socket.``close()?
- socket.``getblocking()?
- socket.``setblocking(flag)?
- socket.``listen([backlog])?
- socket.``send(bytes[, flags])?
- socket.``sendall(bytes[, flags])?
- socket.``sendto(bytes, address)?
- socket.``sendto(bytes, flags, address)
- socket.``recv(bufsize[, flags])?
- socket.``recvfrom(bufsize[, flags]))?
TCP 编程
TCP 客户端和服务器端的交互过程
== 注意: 3.7.3 测试多个 TCP 客户端, 当客户端非正常断开, 服务器程序也会崩掉 ==
例子 1: 简单的 SSH 的 socket 服务器客户端
有几个问题需要注意的:
问题 1:socket 每次接收和发送都有最大数据量限制的, 发送的数据最大量的限制 就是缓冲区能缓存的数据的最大量.
问题 2: 粘包问题, 即服务器端你调用时 send 2 次, 但你 send 调用时, 数据其实并没有立刻被发送给客户端, 而是放到了系统的 socket 发送缓冲区里, 等缓冲区满了, 或者数据等待超时了, 数据才会被 send 到客户端, 这样就把好几次的小数据拼成一个大数据, 统一发送到客户端了, 这么做的目地是为了提高 io 利用效率, 一次性发送总比连发好几次效率高嘛. 但也带来一个问题, 就是 "粘包", 即 2 次或多次的数据粘在了一起统一发送了.
解决办法:
1. 服务器发送数据包的大小, 再发送数据包, 客户端根据数据包的大小判断数据是否完全接收
2. 两次 send 出现的粘包问题
方法 1,time.sleep(0.5) 秒, 缓存区超时, 就会把数据发送给客户端 (不建议)
方法 2, 在两次 send 之间, 服务器设置一个 rev, 客户端设置一个 send. 在客户端不发送确认报文. 服务器端不会主动的发送第 2 次的 sendall 数据, 从而达到两次 send 的数据隔开.
- # 客户端
- import socket
- if __name__ == '__main__':
- server_ip = "localhost"
- server_port = 9999
- client = socket.socket()
- client.connect((server_ip, server_port))
- while True:
- msg = input(">>:").strip()
- if not msg:
- continue
- client.send(msg.encode('utf-8'))
- res_recv_size = client.recv(1024).decode()
- client.send("ACK 应答".encode("utf-8"))
- total_res_size = int(res_recv_size)
- print("收到指令结果的大小:%s" % total_res_size)
- recv_res_size = 0
- cmd_res = b''
- while recv_res_size != total_res_size:
- data = client.recv(1024)
- recv_res_size += len(data)
- cmd_res += data
- print(recv_res_size)
- else:
- print("数据收完了!!")
- print(cmd_res.decode())
- client.close()
- # 服务器端
- import socket
- import os
- if __name__ == '__main__':
- server = socket.socket()
- server.setblocking(True)
- server.bind(("localhost", 9999))
- server.listen(5)
- while True:
- print("等待新连接...")
- conn, addr = server.accept()
- print("新连接接入", addr)
- while True:
- data = conn.recv(1024)
- if not data:
- print("客户端断开连接")
- break
- print("收到数据 %s" % data)
- res = os.popen(data.decode()).read()
- if len(res):
- conn.send(str(len(res)).encode('utf-8'))
- print("等待客户 ack 应答...")
- client_final_ack = conn.recv(1024) # 等待客户端响应
- print("客户应答:", client_final_ack.decode())
- conn.sendall(res.encode("utf-8"))
- else:
- conn.send(b"command error")
- server.close()
UDP 编程
- # 服务器端
- import socket
- if __name__ == '__main__':
- server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- server.bind(("localhost", 9999))
- data, addr = server.recvfrom(1024)
- print("收到数据:", data.decode())
- print("发送人:", addr)
- server.sendto(data, addr)
- server.close()
- # 客户端
- import socket
- if __name__ == '__main__':
- client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- data = b'hello world'
- HOST, PORT = "localhost", 9999
- client.sendto(data, (HOST, PORT))
- data, addr = client.recvfrom(1024)
- print(data.decode(), addr)
- client.close()
- socketserver
- - A framework for network servers?
同步
class socketserver.``TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)
This uses the Internet TCP protocol, which provides for continuous streams of data between the client and server. If bind_and_activate is true, the constructor automatically attempts to invoke server_bind() andserver_activate(). The other parameters are passed to the base class.
- class socketserver.``UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)
- This uses datagrams, which are discrete packets of information that may arrive out of order or be lost while in transit. The parameters are the same as for .
- class socketserver.``UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
- class socketserver.``UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True)
- These more infrequently used classes are similar to the TCP and UDP classes, but use Unix domain sockets; they're not available on non-Unix platforms. The parameters are the same as for .
异步
- class
- socketserver.``ForkingTCPServer
- ?
- class
- socketserver.``ForkingUDPServer
- ?
- class
- socketserver.``ThreadingTCPServer
- ?
- class
- socketserver.``ThreadingUDPServer
- ?
简单 TCP 的 sockeserver
- # 服务器端
- import socketserver
- class Myhandle(socketserver.BaseRequestHandler):
- def handle(self):
- self.data = self.request.recv(1024).decode()
- print(self.data)
- self.request.sendall(self.data.upper().encode("utf-8"))
- if __name__ == '__main__':
- HOST, PORT = "localhost", 9999
- server = socketserver.TCPServer((HOST, PORT), Myhandle) #单线程
- # server = socketserver.ThreadingTCPServer((HOST, PORT), Myhandle) #多线程
- server.serve_forever()
- # 客户端
- import socket
- if __name__ == '__main__':
- server_ip = "localhost"
- server_port = 9999
- client = socket.socket()
- client.connect((server_ip, server_port))
- while True:
- msg = input(">>:").strip()
- if not msg:
- continue
- client.send(msg.encode('utf-8'))
- data = client.recv(1024)
- print(data.decode())
- client.close()
来源: http://www.bubuko.com/infodetail-3115705.html