python 套接字编程的大致流程如下:
server端:
client端 :
在此基础上我们建立一个最基本的服务端,客户端(也就是所谓的cs模型)
server:
#!/usr/bin/env python
#coding:utf-8
#Created by Andy @ 2017/9/16
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8081))
server.listen(5)
conn, client_addr = server.accept()
msg = conn.recv(1024)
print(msg)
conn.close()
server.close()
client
#!/usr/bin/env python
#coding:utf-8
#Created by Andy @ 2017/9/16
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8081))
client.send("hello, server".encode("utf-8"))
client.close()
这大概是最简单的cs模型了,事实上这样只完成了一次从客户端向服务端发送了一条消息,然后就关闭了连接。在实际情况中,服务端应该始终保持链接,通信也就是
上图中的链接循环,通信循环。
我们对上面的代码进行修改:
server:
#!/usr/bin/env python
#coding:utf-8
#Created by Andy @ 2017/9/16
import socket
BUFF_SIZE = 1024
IP_PORT = ("127.0.0.1", 8081)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(IP_PORT)
server.listen(5)
while True:
conn, client_addr = server.accept()
while True:
msg = conn.recv(BUFF_SIZE)
if not msg:
break
else:
conn.send(msg)
msg = msg.decode("utf-8")
print(client_addr, msg)
conn.close()
server.close()
client:
#!/usr/bin/env python
#coding:utf-8
#Created by Andy @ 2017/9/16
import socket
BUFF_SIZE = 1024
IP_PORT = ("127.0.0.1", 8081)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(IP_PORT)
while True:
msg = input(">>:").strip().encode("utf-8")
if not msg:
break
else:
client.send(msg)
response = client.recv(BUFF_SIZE)
if response:
print(response.decode("utf-8"))
client.close()
这样就实现了简单的链接循环,通信循环。
但还有一个问题,就是可能出现粘包,
1.由于tcp的机制,假如我一次只发送了5bytes的数据,它可能只是缓存起来了,并没有直接发送,
等到我再次发送了若干数据(如100bytes)后它将这些数据一起发送出去,这样数据在客户端已经粘包。
2.同样,在服务端解码时也可能出现,也可能出现粘包现象,即将前后多个包一次解码,数据在服务端出现粘包。
那么,要怎么解决呢?
可以看到问题就出在解包时不知道某个包的具体长度是多少,如果我们知道某个包的长度,只需要按照这个包的长度
去解包,即使它粘包了,我们仍然能解出正确的数据来。
那么就需要我们来定制报头,来告知解码的一方这个包的长度:
下面以模拟ssh来做这个例子,
我们约定报头的长度为四个字节。
server:
#!/usr/bin/env python
#coding:utf-8
#Created by Andy @ 2017/9/16
import socket,json, struct, subprocess
BUFF_SIZE = 1024
IP_PORT = ("127.0.0.1", 8081)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)# 重用端口
server.bind(IP_PORT)
server.listen(5)# 设置可以接受的连接数量
while True:# 外层循环为链接循环
conn, client_addr = server.accept()
while True:# 内层循环为通信循环
msg = conn.recv(BUFF_SIZE)
if not msg:
break
pipes = subprocess.Popen(msg.decode("utf-8"),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
error = pipes.stderr.read()
if error:
print("Error:",error)
response_msg = error
else:
response_msg = pipes.stdout.read()
header = {'data_size':len(response_msg)}# 数据长度
header_json = json.dumps(header)#序列化
header_json_byte = bytes(header_json,encoding="utf-8")
conn.send(struct.pack('i',len(header_json_byte)))
#先发送报头长度,仅包含数据长度, 这里的i指int类型
conn.send(header_json_byte)# 再发送报头
conn.sendall(response_msg)# 正式的信息
print("Request from:",client_addr, "Command:",msg)
conn.close()
server.close()
client:
#!/usr/bin/env python
#coding:utf-8
#Created by Andy @ 2017/9/16
import socket, json, struct
BUFF_SIZE = 1024
IP_PORT = ("127.0.0.1", 8081)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(IP_PORT)
while True:
msg = input(">>:").strip().encode("utf-8")
if not msg:
break
client.send(msg)
header = client.recv(4)
print("Header:",struct.unpack("i", header))
header_length = struct.unpack('i', header)[0]
print("Header_length:", header_length)
header_json = json.loads(client.recv(header_length).decode("utf-8"))
data_size = header_json['data_size']
print("Data_size:",data_size)
recv_size = 0
recv_data = b''
while recv_size < data_size:
recv_data += client.recv(BUFF_SIZE)
recv_size += len(recv_data)
print(recv_data.decode("gbk"))
client.close()
大致流程是这样的
事实上,上面的例子只完成了一个客户端与服务器进行通信的功能,并没有实现如
server端中写的server.listen(5),同时与5个客户端通信,要看如何实现可以参考: