Socket网络编程

2024-08-17 01:03:33 浏览数 (3)

前言

在现代计算机网络中,Socket(套接字)是实现进程之间通信的重要工具。在网络应用中,Socket 充当了进程间数据传输的搬运工,负责进程之间的网络数据传输。无论是服务器还是客户端,二者通过 Socket 进行通信,形成了网络应用的基础。本章详细讲解了Socket服务端开发以及Socket客户端开发。


一、Socket网络编程

Socket 是网络编程中用于建立和管理网络连接的一种抽象,主要用于服务端和客户端之间的通信。

①Socket服务端

  • 监听请求: 服务端负责等待并监听来自客户端的连接请求。
  • 接受连接: 每当有客户端连接时,服务端使用 accept() 方法接受该连接,并为该特定连接创建一个新的 Socket 对象。这个连接对象用于与连接的特定客户端进行通信。
  • 处理多个客户端: 服务端可以同时处理多个客户端的请求,每个连接都由一个独立的 conn 对象管理。

②Socket客户端

  • 发起连接: 客户端的作用是发起与服务端的连接请求。它通常只有一个主要的 Socket 对象,用于与服务端进行通信。
  • 一次一连接: 一般来说,客户端一次只会与一个服务端建立连接。

二、Socket服务端编程

①导包并创建socket对象

import socket

socket_server=socket.socket()

②绑定socket_server到指定IP和地址

socket_server.bind((host, port))

  • bind()方法:是 socket 对象的方法,用于将一个地址(主机名和端口号)绑定到一个 Socket 上,通常用于服务器端。
  • 参数(host, port): 一个元组,其中 host 通常是 IP 地址,port 是整数形式的端口号。

【示例】

代码语言:python代码运行次数:0复制
# 绑定socket_server到指定IP和地址
socket_server.bind(("localhost",8888))
  • localhost:一个特殊的主机名,表示本地计算机。它允许服务端在本机上进行测试而不需要连接外部网络。
  • 8888:要绑定的端口号。端口号用于标识特定的服务或进程。在这个例子中,服务端将在本地的 8888 端口上监听来自客户端的连接请求。

③服务端开始监听端口

socket_server.listen(backlog)

  • listen方法:用于将 Socket 设置为被动模式,以便接收来自客户端的连接请求。# 服务端开始监听端口 socket_server.listen(1) # listen()方法内接收一个整数传参数,表示接受的链接数量
  • 参数backlog:为 int 整数,表示允许的连接请求数量。如果同时有多个客户端请求连接,超出该数量的请求将被拒绝。该参数是可选的,如果不指定,系统会自动设置一个合理的默认值。 【示例】

④接收客户端连接,获得连接对象

socket_server.accept()

accept方法:一个阻塞方法,用于在服务器端接受客户端的连接请求。调用该方法后,如果没有客户端尝试连接,accept() 方法会阻塞并等待直到有一个客户端尝试连接。

该方法返回一个二元元组(conn, address),其中:

  • conn:一个新的 Socket 对象,用于与连接的客户端进行通信。
  • address:客户端的地址(IP 地址和端口),提供了连接的客户端的信息。

【示例】

代码语言:python代码运行次数:0复制
# 等待客户端链接
conn,address=socket_server.accept()
"""
上面这行代码等价于
result:tuple=socket_server.accept()
conn=result[0]
address=result[1]
"""

【分析】

accept方法返回的是二元元组(conn, address),可以通过变量1,变量2=socket_server.accept()的形式直接接收二元元组内的两个元素。

⑤接收客户端发送的消息

conn.recv(bytes)

  • recv方法:一个阻塞方法,用于接收连接中发来的数据。如果接收到的数据小于指定的字节数,方法将返回已接收的数据;如果没有数据可接收,程序会等待,直到有数据到达为止。
  • 参数bytes: 一个 int 整数,指定要接收的最大字节数。

【注意】

recv方法通过conn调用而不是socket_server调用。因为conn是专用于与特定客户端进行通信的 Socket 对象,而conn是用于监听和接受连接的服务器 Socket。

【示例】

代码语言:python代码运行次数:0复制
data=conn.recv(1024).decode("UTF-8")
  • recv()方法接收的参数为缓冲区大小,一般给1024即可。
  • recv()方法的返回值为一个字节数组,即bytes对象,不是字符串,可以通过decode()方法进行UTF-8编码,将字节数组转换为字符串对象。

⑥回复客户端信息

conn.send(data)

  • send方法:用于将数据发送给连接的客户端# 通过conn(客户端当次连接对象),调用send方法可以回复消息 conn.send(msg.encode("UTF-8"))在这个示例中,conn.send() 方法接收的参数是通过 encode("UTF-8") 将字符串转换为字节后的结果。这样,客户端才能正确接收和处理该信息。
  • 参数data: 一个字节序列(bytes 对象),表示要发送的数据。 【示例】

⑦关闭连接

conn(客户端当次连接对象)和socket_server对象调用close方法,关闭连接。

conn.close()

socket_server.close()

⑧测试

下载网络调试助手作为客户端进行测试。

下载地址:https://github.com/nicedayzhu/netAssist/releases

服务端代码示例:

代码语言:python代码运行次数:0复制
# 导包
import socket
# 创建socket对象
socket_server=socket.socket()
# 绑定socket_server到指定IP和地址
socket_server.bind(("localhost",8888))
#  服务端开始监听端口
socket_server.listen(1)   # listen()方法内接收一个整数传参数,表示接受的链接数量

# 等待客户端链接
conn,address=socket_server.accept()
print(f"接收到了客户端的链接,客户端信息为{address}")

while True:
    # 接收客户端连接,获得连接对象
    data=conn.recv(1024).decode("UTF-8")
    print(f"客户端发来的消息:{data}")
    
    # 通过conn(客户端当次连接对象),调用send方法可以回复消息
    msg=input("请输入回复客户端的信息:")
    if msg=='exit':
        break
    conn.send()

# 关闭连接
conn.close()
socket_server.close()

运行服务端代码

双击打开安装好的网络调试助手netAssist

配置netAssist相关环境,点击“开始连接”

连接成功

客户端发送消息

服务端成功接收消息

输入 "exit",代码中的 break 语句会被执行,从而终止当前的循环,服务端会停止向客户端发送消息,并最终结束程序。

三、Socket客户端编程

主要分为如下几个步骤:

①导包并创建socket对象

import socket

socket_client=socket.socket()

②连接到服务端

socket_client.connect((host, port))

connect()方法:一个阻塞方法,用于客户端连接到服务器。如果服务器没有响应,connect() 会阻塞,直到连接成功或发生超时。

【示例】

代码语言:python代码运行次数:0复制
# 绑定socket_server到指定IP和地址
socket_client.connect(("localhost",8888))

③发送消息

socket_client.send(data)

代码语言:python代码运行次数:0复制
# 发送消息
socket_client.send("你好呀".encode("UTF-8"))

④接收服务端消息

socket_client.recv(bytes)

代码语言:python代码运行次数:0复制
# 接收返回信息
recv_data=socket_client.recv(1024)   # 1024为缓冲区大小,一般给1024即可

⑤关闭链接

socket_client.close()

四、服务端与客户端相互通讯

服务端示例代码:

代码语言:python代码运行次数:0复制
# 导包
import socket
# 创建socket对象
socket_server=socket.socket()
# 绑定socket_server到指定IP和地址
socket_server.bind(("localhost",8888))
#  服务端开始监听端口
socket_server.listen(1)   # listen()方法内接收一个整数传参数,表示接受的链接数量

# 等待客户端链接
conn,address=socket_server.accept()
print(f"接收到了客户端的链接,客户端信息为{address}")

while True:
    # 接收客户端连接,获得连接对象
    data=conn.recv(1024).decode("UTF-8")
    print(f"客户端发来的消息:{data}")
    
    # 通过conn(客户端当次连接对象),调用send方法可以回复消息
    msg=input("请输入回复客户端的信息:")
    if msg=='exit':
        break
    conn.send()

# 关闭连接
conn.close()
socket_server.close()

客户端示例代码:

代码语言:python代码运行次数:0复制
# 导包
import socket
# 创建socket对象
socket_client=socket.socket()
# 绑定socket_server到指定IP和地址
socket_client.connect(("localhost",8888))
# 发送消息
socket_client.send("你好呀".encode("UTF-8"))
while True:
    # 发送消息
    msg = input("请输入要给服务端发送的消息:")
    if msg == 'exit':
        break
    socket_client.send(msg.encode("UTF-8"))
    # 接收返回消息
    recv_data = socket_client.recv(1024)        # 1024是缓冲区的大小,一般1024即可。 同样recv方法是阻塞的
    print(f"服务端回复的消息是:{recv_data.decode('UTF-8')}")
# 关闭链接
socket_client.close()

先启动服务端再启动客户端。

服务端接收到客户端发来的信息并回复客户端。

客户端接收到服务端发来的信息并回复服务端。

输入 "exit",代码中的 break 语句会被执行,从而终止当前的循环,服务端会停止向客户端发送消息,并最终结束程序。

思考:为什么服务端会比客户端多一个conn对象?

回答:服务端与客户端在通信中扮演着不同的角色。服务端需要接收来自多个客户端的连接,每当一个客户端连入时,服务端通过accept()方法创建一个新的连接对象conn,用于与该特定客户端进行数据交换。这样,服务端可以同时处理多个客户端的请求,而客户端每次只与一个服务端连接。

0 人点赞