开发介绍

TCP 网络应用程序开发分为:

TCP 客户端程序开发 TCP 服务端程序开发

说明:

客户端程序是指运行在用户设备上的程序 服务端程序是指运行在服务器设备上的程序,专门为客户端提供数据服务。  

TCP开发流程示意图

 

 

TCP服务端开发流程:

创建服务端套接字对象:使用socket函数创建一个TCP套接字对象(例如,使用Python中的socket模块创建套接字对象)。 绑定端口号:使用bind函数将服务端套接字绑定到一个特定的IP地址和端口号上。 设置监听:使用listen函数开始监听客户端连接请求。可以指定监听队列的长度,表示可以同时处理的等待连接的最大数量。 等待接受客户端的连接请求:使用accept函数阻塞等待客户端的连接请求。一旦有客户端连接,accept函数会返回一个新的套接字对象,用于与该客户端进行通信。 接收数据:使用recv函数从客户端套接字接收数据。可以指定要接收的数据的最大长度。 发送数据:使用send函数将数据发送给客户端。可以发送指定长度的数据,也可以发送完整的字节串。 关闭套接字:使用close函数关闭服务端套接字和与客户端连接的套接字,释放资源。

需要注意的是,TCP服务端可以是一个循环结构,通过不断地等待新的连接请求和处理已连接客户端的数据来实现长时间运行。在处理多个客户端连接时,可以使用多线程、多进程或异步I/O等技术来实现并发处理。

send和recv

发送接收缓冲区

当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区,这个发送和接收缓冲区指的就是内存中的一片空间。 每个TCP socket都有自己的发送缓冲区和接收缓冲区,它们是独立的,互不干扰。 发送缓冲区和接收缓冲区的大小可以通过设置socket选项来调整,不过一般情况下会使用操作系统默认的大小。这些缓冲区的大小对于TCP的性能和吞吐量有一定的影响,过小的缓冲区可能导致数据丢失或阻塞,而过大的缓冲区可能造成内存资源浪费。

send函数

send函数在应用程序中被调用时,它将数据写入发送缓冲区中,然后由操作系统负责将发送缓冲区中的数据发送到服务端。 send函数会将数据复制到发送缓冲区中,并向操作系统发起发送请求。操作系统会将发送缓冲区的数据复制到网络设备的发送队列中,然后由设备驱动程序控制网卡将数据发送给服务端。 需要注意的是,在发送数据时,操作系统可能会对数据进行分片,并根据网络状况和拥塞情况决定发送的速率。此外,send函数是一个阻塞函数,如果发送缓冲区已满,应用程序的发送操作可能会被阻塞,直到有足够的空间来写入数据。 在实际使用中,建议使用非阻塞I/O或异步I/O来处理发送操作,以避免阻塞整个应用程序的执行。

recv函数

recv函数在应用程序中被调用时,它会从接收缓冲区读取数据,并返回给应用程序使用。 具体来说,当数据到达网络设备时,操作系统会将数据复制到接收队列中,并通知应用程序有数据可读。应用程序调用recv函数时,操作系统将接收缓冲区中的数据复制到应用程序的缓冲区中,并返回实际读取的数据长度。 这种方式将数据接收的过程从应用程序的地址空间转移到操作系统的地址空间,以提高数据接收的效率。操作系统更高效地管理网络设备和底层协议栈,因此能够更好地处理接收数据的任务。 需要注意的是,recv函数是一个阻塞函数,如果接收缓冲区没有数据可读,应用程序的接收操作可能会被阻塞,直到有数据到达并可用。 在实际使用中,建议使用非阻塞I/O或异步I/O来处理发送操作,以避免阻塞整个应用程序的执行。

总结

无论是发送数据还是接收数据,都是通过发送缓冲区和接收缓冲区的中间操作,而最终的数据传输是由操作系统控制网卡来完成的。

发送数据时,应用程序将数据写入发送缓冲区,然后操作系统通过网卡将发送缓冲区的数据发送给对方。接收数据时,操作系统从网卡接收数据,并将数据写入接收缓冲区,应用程序再从接收缓冲区读取数据进行处理。

这种通过缓冲区操作的方式,使得数据的发送和接收能够高效地在应用程序和操作系统之间进行交互,同时也提供了数据的可靠性和可控性。

TCP服务端程序开发

服务端开发步骤

创建服务端端套接字对象 绑定端口号 设置监听 等待接受客户端的连接请求 接收数据 发送数据 关闭套接字

使用python构造服务端

socket 类的介绍

导入 socket 模块

import socket

创建服务端 socket 对象

tcp_server_socket = socket.socket(AddressFamily, Type)

参数说明:

AddressFamily 表示IP地址类型, 分为TPv4和IPv6 Type 表示传输协议类型

tcp_server_socket方法说明:

bind((host, port)) 表示绑定端口号, host 是 ip 地址,port 是端口号,ip 地址一般不指定,表示本机的任何一个ip地址都可以。 listen (backlog) 表示设置监听,backlog参数表示最大等待建立连接的个数。 accept() 表示等待接受客户端的连接请求 send(data) 表示发送数据,data 是二进制数据 recv(buffersize) 表示接收数据, buffersize 是每次接收数据的长度

 

TCP 服务端示例代码

import socket

if __name__ == '__main__':

# 创建tcp服务端套接字

tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 设置端口号复用,让程序退出端口号立即释放

tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

# 给程序绑定端口号

tcp_server_socket.bind(("192.168.1.109", 8989))

# 设置监听

# 128:最大等待建立连接的个数, 提示: 目前是单任务的服务端,同一时刻只能服务与一个客户端,后续使用多任务能够让服务端同时服务与多个客户端,

# 不需要让客户端进行等待建立连接

# listen后的这个套接字只负责接收客户端连接请求,不能收发消息,收发消息使用返回的这个新套接字来完成

tcp_server_socket.listen(128)

# 等待客户端建立连接的请求, 只有客户端和服务端建立连接成功代码才会解阻塞,代码才能继续往下执行

# 1. 专门和客户端通信的套接字: service_client_socket

# 2. 客户端的ip地址和端口号: ip_port

service_client_socket, ip_port = tcp_server_socket.accept()

# 代码执行到此说明连接建立成功

print("客户端的ip地址和端口号:", ip_port)

# 接收客户端发送的数据, 这次接收数据的最大字节数是1024

recv_data = service_client_socket.recv(1024)

# 获取数据的长度

recv_data_length = len(recv_data)

print("接收数据的长度为:", recv_data_length)

# 对二进制数据进行解码

recv_content = recv_data.decode("utf-8")

print("接收客户端的数据为:", recv_content)

# 准备发送的数据

send_data = "ok, 问题正在处理中...".encode("utf-8")

# 发送数据给客户端

service_client_socket.send(send_data)

# 关闭服务与客户端的套接字, 终止和客户端通信的服务

service_client_socket.close()

# 关闭服务端的套接字, 终止和客户端提供建立连接请求的服务

tcp_server_socket.close()

需要注意的是在绑定服务端IP地址时需要使用自己的IP地址,毕竟你是在自己的电脑上操作

tcp_server_socket.bind(("192.168.1.109", 8989))  将192.168.1.109改成自己的IP地址

如何查看电脑的IP地址:

打开cmd命令,输入ipconfig回车

换成自己的IP地址即可继续使用,端口可自由配置。

 

说明::

当客户端和服务端建立连接后,服务端程序退出后端口号不会立即释放,需要等待大概1-2分钟。

解决办法有两种:

更换服务端端口号 设置端口号复用(推荐大家使用),也就是说让服务端程序退出后端口号立即释放。

设置端口号复用的代码如下:

# 参数1: 表示当前套接字

# 参数2: 设置端口号复用选项

# 参数3: 设置端口号复用选项对应的值

tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

小结

导入socket模块 创建TCP套接字‘socket’

参数1: ‘AF_INET’, 表示IPv4地址类型 参数2: ‘SOCK_STREAM’, 表示TCP传输协议类型 绑定端口号‘bind’

参数: 元组, 比如:(ip地址, 端口号) 设置监听‘listen’

参数: 最大等待建立连接的个数 等待接受客户端的连接请求‘accept’ 发送数据‘send’

参数: 要发送的二进制数据, 注意: 字符串需要使用encode()方法进行编码 接收数据‘recv’

参数: 表示每次接收数据的大小,单位是字节,注意: 解码成字符串使用decode()方法 关闭套接字‘socket’表示通信完成

TCP 客户端程序开发

客户端开发步骤

创建客户端套接字对象 和服务端套接字建立连接 发送数据 接收数据 关闭客户端套接字

TCP 客户端程序示例代码

import socket

if __name__ == '__main__':

# 创建tcp客户端套接字

# 1. AF_INET:表示ipv4

# 2. SOCK_STREAM: tcp传输协议

tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 和服务端应用程序建立连接

tcp_client_socket.connect(("192.168.0.156", 8989))

# 代码执行到此,说明连接建立成功

# 准备发送的数据

send_data = "你好服务端,我是客户端小明!".encode("utf-8")

# 发送数据

tcp_client_socket.send(send_data)

# 接收数据, 这次接收的数据最大字节数是1024

recv_data = tcp_client_socket.recv(1024)

# 返回的直接是服务端程序发送的二进制数据

print(recv_data)

# 对数据进行解码

recv_content = recv_data.decode("utf-8")

print("接收服务端的数据为:", recv_content)

# 关闭套接字

tcp_client_socket.close()

同样这里的连接IP地址也需要换成自己的IP地址

说明

str.encode(编码格式) 表示把字符串编码成为二进制 data.decode(编码格式) 表示把二进制解码成为字符串

 

 

 

相关文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: