TCP和UDP的区别:
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
假设是两次握手的情况下:A向B发了一个第一个请求连接报文,此时又于选择了一条较长的路径导致这个请求,传输的时间较长,以致延误到连接释放以后的某个时间才到达server,但是此时A认为发了一个请求连接的报文B没有收到,因此A进行重传,此时这个新的请求连接报文B很快就收到了,于是B向A发送确认报文段,同意连接,于是A和B开始互发数据。但过了一段时间之后B收到了这个延迟的请求连接的报文,此时B立刻发给了A一个确认报文段,同意连接,并且此时B开始等待A给B发数据。但是A认为他刚才并没有发送新的请求连接报文,此时A不会给B发任何数据,那么此时B一直处于等待状态就造成了资源的浪费。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,A不会向B的确认发出确认。B由于收不到确认,就知道A并没有要求建立连接。此时就不会再造成服务器资源的浪费了。
或许有人认为为什么不四次握手或者五次握手呢,但是四次握手和五次握手效果和三次握手是一样的,并且三次握手保证了可靠的同时效率也高啊。
tcp四次断开过程:
1)客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
2)服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
5)客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
因为是双方彼此都建立了连接,因此双方都要释放自己的连接,A向B发出一个释放连接请求,他要释放链接表明不再向B发送数据了,此时B收到了A发送的释放链接请求之后,给A发送一个确认,A不能再向B发送数据了,它处于FIN-WAIT-2的状态,但是此时B还可以向A进行数据的传送。此时B向A 发送一个断开连接的请求,A收到之后给B发送一个确认。此时B关闭连接。A也关闭连接。
参考文章:https://blog.csdn.net/qq_38950316/article/details/81087809
import socket
#1. 创建一个socket对象;
#family指定使用IP协议的版本: IPV4:AF_INET; ipv6: AF_INET6
#type指定传输层使用的协议类型:TCP(SOCKET.SOCK_STREAM), UDP(SOCK_DGRAM)
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
#2. 绑定一个IP和端口, 供客户端来连接;
server.bind(('172.25.254.68', 4002))
#3. 监听是否有客户端连接
server.listen(5)
print("服务端正在启动.........")
#4. 接收客户端的连接, accept() -> (socket object, address info)
clientSocket, address = server.accept()
print("客户端的地址:", address)
#5. 接收客户端发送的消息
recvData = clientSocket.recv(1024)
print("服务端接收的消息:", recvData.decode('utf-8'))
#6. 给客户端回复消息, send(self, data: **bytes***, flags: int = ...) -> int: ...
clientSocket.send('你好, 客户端'.encode('utf-8'))
#7. 关闭服务端socket对象
server.close()
clientSocket.close()
客户端:
import socket
#1. 创建一个socket对象;
#family指定使用IP协议的版本: IPV4:AF_INET; ipv6: AF_INET6
#type指定传输层使用的协议类型:TCP(SOCKET.SOCK_STREAM), UDP(SOCK_DGRAM)
client = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
#连接服务端(ip, port)
client.connect(('172.25.254.68', 4002))
#给服务端回复消息, send(self, data: **bytes***, flags: int = ...) -> int: ...
client.send("你好, 服务端".encode('utf-8'))
#接收服务端回复的消息
recvData = client.recv(1024)
print("客户端接收的消息:", recvData.decode('utf-8'))
#关闭客户端socket对象
client.close()
服务端:
import socket
#1. 创建一个socket对象;
#family指定使用IP协议的版本: IPV4:AF_INET; ipv6: AF_INET6
#type指定传输层使用的协议类型:TCP(SOCKET.SOCK_STREAM), UDP(SOCK_DGRAM)
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
#2. 绑定一个IP和端口, 供客户端来连接;
server.bind(('172.25.254.250', 4006))
#3. 监听是否有客户端连接
server.listen(5)
print("服务端正在启动.........")
#4. 接收客户端的连接, accept() -> (socket object, address info)
clientSocket, address = server.accept()
print("客户端的地址:", address)
while True:
# 5. 接收客户端发送的消息
recvData = clientSocket.recv(1024)
print("服务端接收的消息:", recvData.decode('utf-8'))
# 6. 给客户端回复消息, send(self, data: **bytes***, flags: int = ...) -> int: ...
sendData = input('server:>> ')
if not sendData:
continue
if sendData == 'quit':
print("聊天结束.....")
break
clientSocket.send(sendData.encode('utf-8'))
#7. 关闭服务端socket对象
server.close()
clientSocket.close()
客户端:
import socket
#1. 创建一个socket对象;
#family指定使用IP协议的版本: IPV4:AF_INET; ipv6: AF_INET6
#type指定传输层使用的协议类型:TCP(SOCKET.SOCK_STREAM), UDP(SOCK_DGRAM)
client = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
#2. 连接服务端(ip, port)
client.connect(('172.25.254.250', 4006))
while True:
# 3. 给服务端回复消息, send(self, data: **bytes***, flags: int = ...) -> int: ...
sendData = input('client:>> ')
if not sendData:
continue
if sendData == 'quit':
print("聊天结束.....")
break
client.send(sendData.encode('utf-8'))
# 4. 接收服务端回复的消息
recvData = client.recv(1024)
print("客户端接收的消息:", recvData.decode('utf-8'))
#关闭客户端socket对象
client.close()
服务端:
import os
import threading
from multiprocessing import Process
def get_client_conn(clientSocket, address):
while True:
sendData = os.popen(recvData).read()
clientSocket.send(sendData.encode('utf-8'))
clientSocket.close()
import socket
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
#2. 绑定一个IP和端口, 供客户端来连接;
server.bind(('172.25.254.250', 4008))
#3. 监听是否有客户端连接
server.listen(5)
print("服务端正在启动.........")
while True:
# 4. 接收客户端的连接, accept() -> (socket object, address info)
clientSocket, address = server.accept()
print("客户端的地址:", address)
# 使用多进程
t = Process(target=get_client_conn, args=(clientSocket, address))
t.start()
客户端:
import socket
# 1. 创建一个socket对象;
# family指定使用IP协议的版本: IPV4:AF_INET; ipv6: AF_INET6
#type指定传输层使用的协议类型:TCP(SOCKET.SOCK_STREAM), UDP(SOCK_DGRAM)
client = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
#2. 连接服务端(ip, port)
client.connect(('172.25.254.250', 4007))
while True:
# 3. 给服务端回复消息, send(self, data: **bytes***, flags: int = ...) -> int: ...
sendData = input('client:>> ')
if not sendData:
continue
if sendData == 'quit':
print("聊天结束.....")
break
client.send(sendData.encode('utf-8'))
# 4. 接收服务端回复的消息
recvData = client.recv(1024)
print("客户端接收的消息:", recvData.decode('utf-8'))
#7. 关闭客户端socket对象
client.close()
可以同时和多个客户端进行通信,开启多个客户端,服务端都能接收到
# 实现: udp多个客户端可以连接服务端, 发送命令, 返回的是命令的执行结果;
import socket
# 1. 实例化socket对象
udpServer = socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM)
#2. 绑定IP和端口
#172.25.254.68
#0.0.0.0代表开放所有的IP地址
udpServer.bind(('0.0.0.0', 9001))
print("等待客户端UDP的连接.....")
#3. 接收客户端的连接
recvdata, address = udpServer.recvfrom(1024)
print("接收到客户端的数据: ", recvdata.decode('utf-8'))
#4. 给客户端回复消息
udpServer.sendto(b"hello client", address)
#5. 关闭socket对象
udpServer.close()
客户端:
import socket
udpClient = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
udpClient.sendto(b'hello server', ('172.25.254.68', 9001))
recvData, address = udpClient.recvfrom(1024)
print("接收服务端的数据: ", recvData.decode('utf-8'))
udpClient.close()
服务端:
import socket
import os
import threading
def job():
# 3. 接收客户端的连接
cmd, address = udpServer.recvfrom(1024)
cmd = cmd.decode('utf-8')
print("接收到客户端的命令: ", cmd)
# 4. 给客户端回复消息
cmdRes = os.popen(cmd).read().encode('utf-8')
udpServer.sendto(cmdRes, address)
# 1. 实例化socket对象
udpServer = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
# 2. 绑定IP和端口
# 172.25.254.250
# 0.0.0.0代表开放所有的IP地址
udpServer.bind(('0.0.0.0', 9002))
print("等待客户端UDP的连接.....")
while True:
t = threading.Thread(target=job)
t.start()
#5. 关闭socket对象
#udpServer.close()
客户端:
import socket
udpClient = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
while True:
cmd = input("client:>> ").encode('utf-8')
if not cmd:
continue
if cmd == 'quit':
print("正在退出....")
break
udpClient.sendto(cmd, ('172.25.254.250', 9002))
recvData, address = udpClient.recvfrom(1024)
print("接收服务端的数据: ", recvData.decode('utf-8'))
udpClient.close()
# 实现http客户端的程序, 获取百度页面
import socket
# http://www.baidu.com:80
# 实例化socket对象; 默认参数指定为IPv4协议, 和TCP传输协议;
client = socket.socket()
# 连接服务器端
client.connect(('www.baidu.com', 80))
# 给百度服务器发送请求通过GET方法请求主页内容的请求, http协议的版本为1.1;
client.send(b'GET / HTTP/1.1\r\nHost:www.baidu.com\r\nConnection:close\r\n\r\n')
# 接收服务端返回的页面内容;
recvData = client.recv(1024*100)
# 解码为能够识别的字符串;
print(recvData.decode('utf-8'))
# 关闭客户端连接;
client.close()
import socket
import threading
def handler(clientObj):
# 5. 接收客户端发送的请求
recvData = clientObj.recv(1024)
with open('hello.html', 'rb') as f:
sendData = f.read()
# 告诉浏览器http版本
clientObj.send(b'HTTP/1.1 200 ok\r\n\r\n')
clientObj.send(sendData)
def webServer():
# 1. 创建socket对象
server = socket.socket()
# 2. 绑定端口和ip
server.bind(('0.0.0.0', 8084))
# 3. 监听是否有客户端连接
server.listen(5)
print("http服务正在启动8080........")
while True:
# 4. 接收客户端的连接
clientObj, address = server.accept()
t = threading.Thread(target=handler, args=(clientObj, ))
t.start()
server.close()
if __name__ == '__main__':
webServer()