一、TCP协议示意图:

listen():监听客户端的连接,客户端有链接,listen马上就有返回。
accept():连接之后,会返回客户端的信息(ip地址、端口号),如果没有连接则一直堵塞。连接成功后,服务器就可以发数据,同时会创建一个新的socket
recvfrom:接收客户端的数据,如果服务端没有收到客户端发来的数据,会滞留,下面的代码不会执行。
read():读取客户端发过来的数据
write():将数据返回给客户端
由new_socket来完成与客户端数据的传输
如果new_socket关闭,则说明当前的这个客户端请求结束,服务端服务完毕;如果有新的请求来了,服务器会创建新的new_socket与它通讯。
如果关掉第一个socket,则说明整个服务端程序关闭了

1、客户端与服务端进行通信之前必须建立连接;
2、客户端先调用connect()方法(在应用层),服务端执行accept()方法(在应用层),3次握手操作(在传输层),建立tcp连接
3、客户端和服务端进行通信(字节流数据)
4、通信结束后,客户端和服务端都要进行关闭,调用close()方法(在应用层),进行4次挥手(在传输层),关闭tcp连接

二、TCP三次握手

为了保证客户端和服务端的可靠连接,TCP建立连接时必须要进行三次会话(发送3次数据完成整个连接的建立),也叫TCP三次握手。
进行三次握手的目的时为了确认双方的接收能力和发送能力是否正常

当客户端调用connect()函数时,开始进行3次握手
首先客户端向服务端发送一个请求数据包;证明客户端的发送能力正常
服务端收到客户端的请求数据包后,向客户端发送一个应答的数据包;证明服务端的接收能力、发送能力正常
客户端收到服务端的应答数据包后,紧接着向服务端发送一个应答的数据包;证明客户端的接收能力正常

三、TCP四次挥手

当客户端socket调用close()方法,进行四次挥手
首先客户端发送一个请求释放的数据包告诉服务器我要关闭连接了;属于主动数据包
服务器收到客户端发送过来的结束连接的数据包后,向客户端发送一个应答
紧接着向客户端发送一个关闭连接的数据包,通知客户端我也要结束了
客户端也收到了服务端释放连接的数据包后,客户端再向服务端发送一个应答

为什么客户端要等待2MSL?

保证客户端发送的第一个ACK报文能到服务器,因为ACK报文可能会丢失,并且2MSL是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃,这样新的连接中不会出现旧连接的请求报文。

二、步骤

(1) socket创建一个套接字
(2) bind绑定ip和port
(3) listen使套接字变为可以被动链接
(4) accept等待客户端的链接
(5) recv/send接收发送数据

三、代码实现TCP协议

服务器端

from socket import *

# todo 1、创建server-socket      TCP协议
server_socket = socket(AF_INET, SOCK_STREAM)

# todo 2、创建目标服务器,绑定一个IP和端口  服务器里面空的字符串代表server_socket绑定这台机器下的任何ip地址
host_port = ('', 8088)
server_socket.bind(host_port)

# todo  3、监听服务器的socket,listen让socket处于被动。这时就可以接收客户端的连接请求了
server_socket.listen(5)  # 一般为5,表示客户端线程挂起的个数

# todo 4、等待客户端的连接请求,当前函数是线程阻塞的函数,accept返回2个值,第一个:新的socket,第二个:客户端地址
new_socket, client_addr = server_socket.accept()  # todo 新创建的socket是server_socket中的子socket,只是和当前的客户端(一个客户端)收发数据

# todo 5、服务器接收客户端发送过来的数据,recv一般用于TCP协议的接收数据,recvfrom用于UDP
data = new_socket.recv(1024)  # data是字节数据

print('服务器接收的数据是:', data.decode('utf-8'))

# todo 6、服务器端发送数据给客户端,在TCP中发送数据用send,UDP用sendto
new_socket.send('THANK you'.encode('utf-8'))

# 关闭套接字
new_socket.close()  # todo  new_socket关闭意味着当前客户端的服务已经完成
server_socket.close()  # todo  server_socket关闭意味着整个服务器全部关闭

代码注释:

1、host_port = (‘’, 8088)
server_socket.bind(host_port)
创建目标服务器,绑定一个IP和端口 服务器里面空的字符串代表server_socket绑定这台机器下的任何ip地址
2、new_socket, client_addr = server_socket.accept()
等待客户端的连接请求,当前函数是线程阻塞的函数,accept返回2个值,第一个:新的socket,第二个:客户端地址,
新创建的socket是server_socket中的子socket,只是和当前的客户端(一个客户端)收发数据
3、data = new_socket.recv(1024) # data是字节数据
服务器接收客户端发送过来的数据,recv一般用于TCP协议的接收数据,recvfrom用于UDP
4、new_socket.send(‘THANK you’.encode(‘utf-8’))
服务器端发送数据给客户端,在TCP中发送数据用send,UDP用sendto

客户端:

from socket import *

# todo 1、客户端、创建client-socket    TCP协议
client_socket = socket(AF_INET, SOCK_STREAM)

# todo  2、客户端发送连接的请求(不是传输数据的请求,是创建连接的请求)

# todo  目标服务器的ip和端口号
server_ip_port = ('192.168.1.112', 8088)

# todo 发送连接请求,此时服务端产生了新的new_socket
client_socket.connect(server_ip_port)

#client_socket.close()

# todo 客户端发送请求,用send不用sendto,客户端知道服务器的ip和端口,服务器也知道客户端的端口和ip,因为是面向连接的
send_data = input('请输入:')
client_socket.send(send_data.encode('utf-8'))

# todo  接收服务器返回的数据
recv_data = client_socket.recv(1024)

print('客户端接收到的服务器的数据为:', recv_data.decode(encoding='utf-8'))

# todo 关闭socket
client_socket.close()

代码注释:
send_data = input(‘请输入:’)
client_socket.send(send_data.encode(‘utf-8’))
客户端发送请求,用send不用sendto,客户端知道服务器的ip和端口,服务器也知道客户端的端口和ip,因为是面向连接的

更多推荐

python网络编程—TCP协议(一)