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
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 theBaseServer
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
TCPServer
. -
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
TCPServer
.
异步
- 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()