笔记
昨日内容回顾 1 网络协议 osi七层 应表会传网数物 tcp\ip五层 传输层:TCP\UDP协议 TCP:面向连接,消息可靠,相对效率低,消息格式是面向流的,无消息保护边界 UDP:无连接,消息不可靠,效率高,面向包的,有消息保护边界 tcp 三次握手: 四次挥手: 2 tcp协议下的socket udp协议下的socket 今日内容: tcp:属于长连接,与一个客户端进行连接了以后,其他的客户端要等待,要连接另外一个,必须优雅的断开前面这个客户端的连接. 允许地址重用:在bind IP地址和端口之前加上,# server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 允许(IP地址和端口)地址重用 缓冲区 输入缓冲区 #recv 输出缓冲区 #send 粘包(tcp的两种粘包现象) 1 连续发送小的数据,并且每次发送之间的时间间隔很短(输出缓冲区:两个消息在缓冲区黏在一起了) 原因是TCP为了传输效率,做了一个优化算法(Nagle),减少连续的小包发送(因为每个消息被包裹以后,都会有两个过程:1 组包 2拆包) 2 第一次服务端发送的数据比我客户端设置的一次接收消息的大小要大,那么接收不完,第二次再接收的时候,就会将第一次剩余的消息接收到 粘包的根本原因是因为:双方不知道对方发送消息的大小 解决方案一: 发送消息之前,先计算要发送消息的长度,然后先将消息长度发送过去,对方给你回一个确认收到长度的信息,然后根据接收到的消息长度来修改自己一次接收消息的大小 这个过程多了一次交互 明天预习: 粘包解决方案二 合法性链接 ftp那个代码实例 socketserver这个模块
account.txt
alex|dsb
sub_test.py
import subprocess
sub_obj = subprocess.Popen('dir',stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
print(sub_obj.stdout.read().decode('gbk'))
test.py
#作业简单讲解
# 1.时间戳转换为格式化时间的字符串
import time
while 1:
t1 = time.time()
print(t1)
t1_stru = time.localtime(t1)
print(time.strftime('%Y/%m/%d',t1_stru))
t1_ss = time.strftime('%Y/%m/%d',t1_stru)
time.sleep(10)
client.send(t1_ss)
udp_client.py
import socket
BUFSIZE=1024
udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
qq_name_dic={
'taibai':('127.0.0.1',8081),
'Jedan':('127.0.0.1',8081),
'Jack':('127.0.0.1',8081),
'John':('127.0.0.1',8081),
}
while True:
qq_name=input('请选择聊天对象: ').strip()
while True:
msg=input('请输入消息,回车发送,输入q结束和他的聊天: ').strip()
if msg == 'q':break
if not msg or not qq_name or qq_name not in qq_name_dic:continue
udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])# 必须带着自己的地址,这就是UDP不一样的地方,不需要建立连接,但是要带着自己的地址给服务端,否则服务端无法判断是谁给我发的消息,并且不知道该把消息回复到什么地方,因为我们之间没有建立连接通道
back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)# 同样也是阻塞状态,等待接收消息
print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))
udp_client_socket.close()
udp_sever.py
import socket
ip_port=('127.0.0.1',8081)
udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #DGRAM:datagram 数据报文的意思,象征着UDP协议的通信方式
udp_server_sock.bind(ip_port)#你对外提供服务的端口就是这一个,所有的客户端都是通过这个端口和你进行通信的
while True:
qq_msg,addr=udp_server_sock.recvfrom(1024)# 阻塞状态,等待接收消息
print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))
back_msg=input('回复消息: ').strip()
udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
练习
1、什么缓冲区,为什么会有缓冲区? 2、写一个tcp写一下的socket对话程序,能够优雅的断开,让另外一个客户端连接上进行收发消息。 3、自行完成今天课上写的使用subprocess进行系统调用的对话方式,客户端发送指令,服务端执行指令并且将结果返回给客户端,确保不产生粘包 4、什么是粘包? socket 中造成粘包的原因是什么? 哪些情况会发生粘包现象? 5、用tcp协议下的socket,写一个简易的文件上传下载的功能,用户需要登陆认证,认证成功后,客户端用户可以选择上传或者是下载, 上传的时候服务端提前设定好上传文件的路径,将文件上传到对应的路径下,下载文件的时候,服务端将之前设定好的上传路径中的所有 文件带上序号展示给用户看,用户输入文件序号后下载对应的文件,文件下载到客户端程序的当前路径下就可以了。
client.py
import socket
client = socket.socket()
server_ip_port = ('192.168.15.113',8001)
#链接服务端
client.connect(server_ip_port)
while 1:
username = input('请输入用户名:')
password = input('请输入密码:')
# 按照服务端要求的信息格式发送
user_info = username + '|' + password
client.send(user_info.encode('utf-8'))
if user_info == 'good|bye':
break
from_server_msg = client.recv(1024).decode('utf-8')
print(from_server_msg)
if from_server_msg == '登录成功':
break
client.close()
sever.py
import socket
#创建了一个socket对象
server = socket.socket() #创建一个手机
# server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 允许(IP地址和端口)地址重用
ip_port = ('192.168.15.113',8001) #创建了一张电话卡
#绑定IP地址和端口
server.bind(ip_port) #插上电话卡
#监听IP地址和端口
server.listen() #开机,后面等待链接的客户端个数为3个
print(11111)
while 1:
#等待客户端的链接,
conn, addr = server.accept() #等着别人给我打电话,阻塞住
flag = 0
while not flag:
user_info = conn.recv(1024).decode('utf-8') # alex|dsb
print('客户端说>>>',user_info)
if user_info == 'good|bye':
break
with open('account', 'r', encoding='utf-8') as f:
for i in f:
if i.strip() == user_info:
conn.send('登录成功'.encode('utf-8'))
flag = 1
break
else:
conn.send('用户名或者密码错误'.encode('utf-8'))
# conn, addr = server.accept()
print('准备断开连接!')
conn.close()
# server.close()
# print(conn)
# print('>>>>',addr)
粘包现象1客户端.py
import socket
client = socket.socket()
server_ip_port = ('192.168.15.113',8001)
client.connect(server_ip_port)
client.send('hello'.encode('utf-8'))
client.send('sigui'.encode('utf-8'))
client.close()
#第二种粘包现象描述
#客户端第一次发送了一个2000B大小的数据
#第二次发送了一个1000B大小的数据
#由于服务的第一次接受数据的大小位1024B,导致剩余的976B的数据,在服务端第二次接受的数据中了
#根本原因:服务端不知道客户端发送数据的大小是多少
粘包现象1服务端.py
import socket
server = socket.socket()
ip_port = ('192.168.15.113',8001)
server.bind(ip_port)
server.listen()
conn,addr = server.accept()
from_client_msg1 = conn.recv(1024).decode('utf-8')
#2000B -- 1024 976B + 1000B
from_client_msg2 = conn.recv(1024).decode('utf-8')
#976+48 = 1024
print('msg1:',from_client_msg1)
print('msg2:',from_client_msg2)
conn.close()
server.close()
粘包现象2客户端.py
import socket
client = socket.socket()
server_ip_port = ('192.168.15.113',8001)
client.connect(server_ip_port)
while 1:
msg = input('请输入要执行的指令>>>')
client.send(msg.encode('utf-8'))
#先接收服务端要发送给我的信息的长度
from_server_msglen = int(client.recv(1024).decode('gbk'))
print('..........',from_server_msglen)
#给服务端回应一个收到了你的信息长度的确认信息
client.send('ok'.encode('utf-8'))
#拿到信息长度后,将信息长度作为参数给了recv,recv就按照这个长度大小来接受服务端后面要给我发送的数据
from_server_stdout = client.recv(from_server_msglen).decode('gbk')
print('收到的正确信息:', from_server_stdout)
# from_server_error = client.recv(1024).decode('utf-8')
# print('收到的错误信息:',from_server_error)
粘包现象2服务端.py
import socket
import subprocess
server = socket.socket()
ip_port = ('192.168.15.113',8001)
server.bind(ip_port)
server.listen()
conn,addr = server.accept()
while 1:
#来自客户端的指令
print('等待接受信息。。。')
from_client_cmd = conn.recv(1024).decode('utf-8')
print(from_client_cmd)
sub_obj = subprocess.Popen(
from_client_cmd, #客户端的指令
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
#接受到的返回信息是bytes类型的,并且windows系统的默认编码为gbk
server_cmd_msg = sub_obj.stdout.read()
# server_cmd_err = sub_obj.stderr.read().decode('gbk')
cmd_msg_len = str(len(server_cmd_msg))
print('指令返回的正确信息的长度>>>',cmd_msg_len)
# print('指令返回的正确信息>>>',server_cmd_msg)
# print('指令返回的错误信息...',server_cmd_err)
conn.send(cmd_msg_len.encode('gbk'))
from_client_ack = conn.recv(1024).decode('utf-8')
print('from_client_ack',from_client_ack)
if from_client_ack == 'ok':
conn.send(server_cmd_msg)
else:
continue
# conn.send(server_cmd_err.encode('utf-8'))