1、什么缓冲区,为什么会有缓冲区?缓冲区是指socket通信时,收发命令时的一个中间存放命令的存储空间因为数据被输出后在处理的时候需要一定的时间,
为了输入接着输入零时差,就需要缓冲了,先预读并处理一部分信息,然后开始输出,在输出的同时进行后面的的处理,然后等缓冲的部分输出完后,另一部分的数据也处理完毕了,
就可以接着输出了,如果没有这个缓冲区,就会很卡输入输出的缓冲区一般是8k
tcp粘包演示(一):
import subprocess
ip_port=('127.0.0.1',8080)
BUFSIZE=1024
tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)
while True:
conn,addr=tcp_socket_server.accept()
print('客户端>>>',addr)
while True:
cmd=conn.recv(BUFSIZE)
if len(cmd) == 0:break
res=subprocess.Popen(cmd.decode('gbk'),shell=True,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
stderr=res.stderr.read()
stdout=res.stdout.read()
conn.send(stderr)
conn.send(stdout)
size = 1024
tcp_sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res = tcp_sk.connect(ip_port)
while True:
msg=input('>>: ').strip()
if len(msg) == 0:continue
if msg == 'quit':break
tcp_sk.send(msg.encode('utf-8'))
act_res=tcp_sk.recv(size)
print('接收的返回结果长度为>',len(act_res))
print('std>>>',act_res.decode('gbk')) #windows返回的内容需要用gbk来解码,因为windows系统的默认编码为gbk
tcp粘包演示(二):发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据也很小,会合到一起,产生粘包)
server端代码示例:(如果两次发送有一定的时间间隔,那么就不会出现这种粘包情况,试着在两次发送的中间加一个time.sleep(1))
from socket import *
ip_port=('127.0.0.1',8080)
tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)
conn,addr=tcp_socket_server.accept()
data1=conn.recv(10)
data2=conn.recv(10)
print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))
conn.close()
client端代码示例:
import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# res=s.connect_ex(ip_port)
res=s.connect(ip_port)
s.send('hi'.encode('utf-8'))
s.send('meinv'.encode('utf-8'))
udp粘包演示:注意:udp是面向包的,所以udp是不存在粘包的
粘包的原因:主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
粘包的解决方案
解决方案(一):
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(ip_port)
s.listen(5)
while True:
conn,addr=s.accept()
print('客户端',addr)
while True:
msg=conn.recv(1024)
if not msg:break
res=subprocess.Popen(msg.decode('utf-8'),shell=True,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
err=res.stderr.read()
if err:
ret=err
else:
ret=res.stdout.read()
data_length=len(ret)
conn.send(str(data_length).encode('utf-8'))
data=conn.recv(1024).decode('utf-8')
if data == 'recv_ready':
conn.sendall(ret)
conn.close()
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
if len(msg) == 0:continue
if msg == 'quit':break
s.send(msg.encode('utf-8'))
length=int(s.recv(1024).decode('utf-8'))
s.send('recv_ready'.encode('utf-8'))
send_size=0
recv_size=0
data=b''
while recv_size < length:
data+=s.recv(1024)
recv_size+=len(data)
print(data.decode('utf-8'))
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
while True:
conn,addr=phone.accept()
while True:
cmd=conn.recv(1024)
if not cmd:break
print('cmd: %s' %cmd)
res=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
err=res.stderr.read()
print(err)
if err:
back_msg=err
else:
back_msg=res.stdout.read()
headers={'data_size':len(back_msg)}
head_json=json.dumps(headers)
head_json_bytes=bytes(head_json,encoding='utf-8')
conn.send(struct.pack('i',len(head_json_bytes))) #先发报头的长度
conn.send(head_json_bytes) #再发报头
conn.sendall(back_msg) #在发真实的内容
conn.close()
import struct,json
ip_port=('127.0.0.1',8080)
client=socket(AF_INET,SOCK_STREAM)
client.connect(ip_port)
while True:
cmd=input('>>: ')
if not cmd:continue
client.send(bytes(cmd,encoding='utf-8'))
head=client.recv(4)
head_json_len=struct.unpack('i',head)[0]
head_json=json.loads(client.recv(head_json_len).decode('utf-8'))
data_len=head_json['data_size']
recv_size=0
recv_data=b''
while recv_size < data_len:
recv_data+=client.recv(1024)
recv_size+=len(recv_data)
#print(recv_data.decode('utf-8'))
print(recv_data.decode('gbk')) #windows默认gbk编码
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
self.request.sendall(self.data.upper())
if __name__ == "__main__":
HOST, PORT = "127.0.0.1", 9999
# 设置allow_reuse_address允许服务器重用地址
socketserver.TCPServer.allow_reuse_address = True
# 创建一个server, 将服务地址绑定到127.0.0.1:9999
#server = socketserver.TCPServer((HOST, PORT),Myserver)
server = socketserver.ThreadingTCPServer((HOST, PORT),Myserver)
# 让server永远运行下去,除非强制停止程序
server.serve_forever()
HOST, PORT = "127.0.0.1", 9999
data = "hello"
# 创建一个socket链接,SOCK_STREAM代表使用TCP协议
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((HOST, PORT)) # 链接到客户端
sock.sendall(bytes(data + " ", "utf-8")) # 向服务端发送数据
received = str(sock.recv(1024), "utf-8")# 从服务端接收数据
print("Sent: {}".format(data))
print("Received: {}".format(received))
4、什么是粘包? socket 中造成粘包的原因是什么? 哪些情况会发生粘包现象?
TCP粘包是指发送方发送的若干包数据包到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾
(1)发送方原因
TCP默认会使用Nagle算法。而Nagle算法主要做这两件事1>只有上一个分组得到确认,才会发送下一个分组;2>收集多个小分组,再确认到来时一起发送
所以是Nagle算法造成了发送方造成了粘包现象
(2)接收方原因
TCP接收到分组时,并不会立刻送至应用层处理,或者说,应用层并不会立即处理;实际上,TCP将收到的分组保存至接收缓存里,然后
应用程序主动从缓存里读收到的分组。这样一来,如果TCP接收分组的速度大于应用程序读分组的速度,多个包就会存至缓存,应用程序
读时,就会读到多个首尾相接粘到一起的包
哪些情况会发生粘包现象?
(1)发送端需要等待缓冲区满才发送出去,造成粘包
(2)接收方不及时接收缓冲区的包,造成多个包接收
5、用tcp协议下的socket,写一个简易的文件上传下载的功能,用户需要登陆认证,认证成功后,客户端用户可以选择上传或者是下载,
上传的时候服务端提前设定好上传文件的路径,将文件上传到对应的路径下,下载文件的时候,服务端将之前设定好的上传路径中的所有
文件带上序号展示给用户看,用户输入文件序号后下载对应的文件,文件下载到客户端程序的当前路径下就可以了。