用socket实现文件(文本文档,图片,视频)的上传和下载
1.客户端给服务器发送一个文件名,服务器去当前文件中去查找这个文件夹,并把他的内容读取出来,发送个客户端,客户端接受并保存
客户端:
1 import socket 2 from threading import Thread 3 4 def main(): 5 tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 tcp_client.connect(("127.0.0.1", 9001)) # 服务器的端口和地址以一个tuple()的形式 8 data = input("请输入要下载的文件名:") 9 tcp_client.send(data.encode("utf-8")) 10 info = tcp_client.recv(1024 * 1024) 11 print(info) # 只能打印utf-8编码内的 12 if info: 13 with open("T" + data, "wb") as file: 14 file.write(info) 15 tcp_client.close() 16 17 if __name__ == '__main__':
#创建一个线程用来多次连接 18 for i in range(10): 19 t = Thread(target=main, args=(i,)) target用来接受函数名,args用来传参 20 t.start() 21 t.join()
服务端
1 import socket 2 3 def send_file_2_client(new_client_socket, client_addr): 4 filename = new_client_socket.recv(1024).decode("utf-8") 5 print("客户端%s需要下载的文件是%s" % (str(client_addr), filename)) 6 file_content = None 7 try: 8 file = open(filename, "rb") 9 file_content = file.read() 10 file.close() 11 except Exception as e: 12 print("当前没有这个文件%s" % str(e)) 13 if file_content: 14 new_client_socket.send(file_content) 15 def main(): 16 tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 17 tcp_server.bind(("127.0.0.1", 9001)) 18 tcp_server.listen(128) 19 while True: 20 new_client_socket, client_addr = tcp_server.accept() # accept返回的是一个新的socket的通信new_client_socket和一个地址client_addr 21 send_file_2_client(new_client_socket, client_addr) # 调用文件发送数据方法 22 new_client_socket.close() 23 tcp_server.close()
TCP通信的特点:安全,可靠 TCP通信的过程:创建连接(三次握手) + 发送数据 + 断开连接(四次挥手)
位码即tcp标志位,有6种标示:
SYN(synchronous建立联机) ACK(acknowledgement 确认)
PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)
Sequence number(顺序号码) Acknowledge number(确认号码)
三次握手: 第一次握手:客户端向服务器发送连接请求包,标志位SYN(同步序号)置为 seq = X; 第二次握手:服务器收到客户端发过来报文,由 SYN = X知道客户端要求建立联机。向客户端发送一个包含SYN和ACK的TCP报文, 其中服务器向客户端发送连接请求包,标志位SYN(同步序号)置为 Y=1;将确认序号(ACK)设置为客户的序列号加1,即 X+1 , 第三次握手:客户端收到服务器发来的包后检查确认序号(ACK)是否正确,即第一次发送的序号加1(X+1);若正确,服务器再次发送确认序号(ACK) = Y+1; 服务器收到确认序号值 ACK=2 则连接建立成功,可以传送数据了。
四次挥手: 第一次挥手:客户端给服务器发送TCP包,用来关闭客户端到服务器的数据传送。将标志位FIN 置为 X 和ACK置为 Y; ->客户端close() 第二次挥手:服务器收到FIN后,发回一个ACK(标志位ACK=1),确认序号为收到的序号加1,即 X+1; ->服务器send()关闭 第三次挥手:服务器关闭与客户端的连接,发送一个FIN,标志位FIN和ACK置为1,序号为 Y; ->服务器close() 第四次挥手:客户端收到服务器发送的FIN之后,发回ACK确认,确认序号为收到的序号加1,即Y+1,服务器收到ACK为Y+1后关闭; ->客户端send()关闭 为什么是三次握手: 三次握手可以简单看做是客户发送请求,服务器对客户的请求并进行确认,客户对服务器的确认再进行确认;好处:减少连接时长 如果采用两次握手,假设下面这种情况,客户向服务器发送请求,服务器没有对客户的请求进行确认(因为网络的延迟他可能没有收到这个请求) 客户收不到这个确认于是过一段时间他在向服务器发起连接请求并顺利完成数据传输, 但是过了一段时间这个请求到达了服务器而服务器误以为这是一个新的连接请求,于是对这个请求进行确认并发送确认给客户, 但是客户没有发起过连接请求因此它不会理会服务器的确认,服务器以为这个连接已经建立好了于是一直等待客户发送数据,这样就会造成服务器的资源浪费。 如果采用三次握手上述情况客户不会向服务器的确认进行确认,这样服务器收不到确认它就知道客户没有要发起请求的连接,于是不会再等待。 三次握手主要是为了防止已失效的连接请求报文突然到达服务器,造成服务器的等待和资源的浪费。 为什么是四次挥手: 在三次握手的过程中,SYN和ACK是一起发送的,但是在四次挥手的时候FIN和ACK却不是一起发送的而是分开发送的,为什么呢? 那是因为TCP连接是全双工的,也就是说接收到FIN只是说没有数据再发过来,但是还是可以发送数据的,也就是接受到一个FIN只是关闭了一个方向的数据传输, 另一个方向还可以继续发送数据。在四次挥手的时候也是这样前两次挥手只是确认关闭了一个方向的数据,加上后面两次挥手才真正的关闭了整个全双工连接。 当socket在ESTABISHED/estabished "建立"状态时,他想主动关闭连接于是向对方发送FIN请求,发送完FIN请求后它处于FIN_WAIT_1状态,当对方确认ACK报文后则处于FIN_WAIT_2状态。 FIN_WAIT_2表示半连接,也就是有一方要求关闭连接,另一方收到请求但是告诉她我还有一些数据要发送稍后会关闭。TIME_WAIT状态表示收到对方的FIN并发送出ACK。 如果三次挥手可能在关闭后还有一个方向没有关闭。 2MSL: 2MSL即两倍的MSL,TCP的客户端在第四次挥手后并没有直接close关闭,而是进入TIME_WAIT状态,也称为2MSL等待状态, 为什么要有2MSL等待状态? 当TCP的一端发起主动关闭,在发出最后一个ACK包后, 即第3次挥手完成后发送了第四次挥手的ACK包后就进入了TIME_WAIT状态, 必须在此状态上停留两倍的MSL时间, 等待2MSL时间主要目的是怕最后一个 ACK包对方没收到, 那么对方在超时后将重发第三次挥手的FIN包, 主动关闭端接到重发的FIN包后可以再发一个ACK应答包。 在TIME_WAIT状态 时两端的端口不能使用,要等到2MSL时间结束才可继续使用。 当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。 RFC 793 中 MSL规定为2分钟,两倍的MSL即为4分钟, 但是实际应用中一般为30秒,1分钟或2分钟不等。