二、socket网络服务器的简单编写与测试

上一章我们学习了如何编写网络客户端,建立socket套接字对象连接到服务器,并和服务器通信。接下来我们将介绍如何编写网络服务器、如何获取客户端的信息、 如何把活动记入日志,以及用不同方式来运行服务器。

2.1 准备连接

对于一个客户端来说,建立一个TCP连接的过程大致可分两步,即建立socket对象以及调用connect()来与服务器建立连接。对于服务器来说,这个过程分为如下四步:

2.1.1建立socket对象。

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

2.1.2 设置socket选项(可选)。

python提供了setsockopt()和getsockopt()方法分别用于设置和获取套接字选项。

# 设置套接字选项,允许重新使用地址和端口。
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

socket.SOL_SOCKET :表示设置的是套接字级别的选项。
socket.SO_REUSEADDR :选项名称,表示允许地址重用。
1 :表示True,是选项的值,表示启用该选项。

通常情况下,当服务器程序关闭后,会保留一段时间的套接字,即使服务器已经关闭,这段时间内新的套接字无法绑定到相同的地址和端口上。如果希望在服务器程序关闭后立即重新启动,并绑定到相同的地址和端口上,就可以使用 SO_REUSEADDR 选项

# 获取套接字超时时间
timeout = server_socket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO)
print('Receive timeout:', timeout)

SOL_SOCKET常用到的选项

选项级别功能说明
SO_REUSEADDR允许地址重用。允许一个套接字绑定到一个已在使用中的地址,通常用于服务器程序在重启后快速重新启动。
SO_KEEPALIVE开启或关闭 TCP 的 keep-alive 功能。当启用时,套接字会周期性地检测连接是否仍然活动。
SO_RCVBUF设置接收缓冲区大小
SO_SNDBUF设置发送缓冲区的大小
SO_REUSEPORT允许多个套接字绑定到相同的端口上。通常用于多进程或多线程服务器,提高并发连接的性能。
SO_BROADCAST允许发送广播数据包。
SO_ERROR获取套接字错误状态。
SO_LINGER设置关闭套接字时的行为。如果开启了 SO_LINGER,并设置了超时时间,那么当调用 close() 方法时,套接字会等待一段时间,直到发送或接收缓冲区中的数据被发送或超时。
SO_REUSEPORT允许多个套接字绑定到相同的端口上通常用于负载均衡或者多进程/多线程服务器。

我们可以通过以下代码列出python所支持的选项列表

import socket
# 返回socket模块中所有属性和方法,并过滤出以‘SO_’开头的常量
socket_list = [x for x in dir(socket) if x.startswith('SO_')]
socket_list.sort()
for sockets in socket_list:
    print(sockets)

2.1.3 绑定到一个端口。

server_socket.bind(('', 12345))

bind()函数的第一个参数是要绑定的IP地址,此处为空,表示可以绑定到所有接口和地址,第二个参数指定服务器监听连接的端口号

2.1.4 监听连接。

server_socket.listen(5)

通过调用listen()函数通知操作系统准备接收连接。它只有一个参数,指明服务器在实际处理连接的时候,允许有多少个未决的连接在队列中等待。

2.2 接受连接

大多数服务器都设计成运行不确定时间(几个月甚至几年)和同时服务于多个连接。与此相反,客户端一般只有几个连接,并且会运行到任务完成或用户终止。
**通常使服务器持续运行的方法是使用一个无限循环。**如下例子:

while True:
    client_sock, client_addr = server_socket.accept()
    print('Connection from:', client_sock.getpeername())
    client_sock.close()

通常情况下,无线循环是不好的,因为它会耗尽系统CPU的资源。但是,此处的无限循环是不同的,此处accept()是一个阻塞方法,用于等待客户端连接的请求。具体来说,accept()方法会阻塞程序的执行,直到有客户端尝试连接到服务器。一旦有客户端连接请求到达,该方法会返回一个包含两个元素的元组连接到客户端的新套接字对象客户端地址。一个停止并等待输入或输出的程序被称为阻塞的程序。

2.3 简单TCP服务器的实现及测试

以下是一个简单TCP服务器实现的完整代码,可用于同时处理多个客户端连接,但每个连接都是单线程的,即每个连接都是依次处理的。

import socket

# 1.建立socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.设置socket选项(可选)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 3.绑定到一个端口
host = ''  # 服务器地址,空字符串表示本机所有网络接口
port = 12345
server_socket.bind((host, port))
# 4.监听连接
server_socket.listen(5)  # 最多允许5个连接
print(f'Server started on {host}:{port}')
while True:
    # 接受客户端连接
    client_socket, client_addr = server_socket.accept()
    # 向客户端发送成功连接信息
    client_socket.send(b'Successful Connection!\r\n')
    print('Connected by', client_addr)
    # 处理客户端请求
    while True:
        data = client_socket.recv(1024)
        if not data:
            break
        print('Received:', data.decode('utf-8'))
        # 回复客户端
        client_socket.send(b'\r\nHello from server!')
    client_socket.close()

现在对这个服务器程序进行测试,可以使用操作系统的telnet指令。具体步骤如下(以Windows系统为例):

  1. 运行程序运行程序
  2. 打开cmd终端,输入指令 telnet localhost 12345

Telnet 是一种网络协议,允许用户通过 TCP/IP 网络连接到远程主机,并在远程主机上执行命令。通常用于远程登录和远程管理。这条指令的意思是连接到本地主机的12345端口。执行本条指令时注意要打开本机telnet远程服务,具体如何打开此处不作表述,请有需要的读者自行网上查阅。

本机cmd终端
本机cmd终端
此时我们输入任意一个字符,我们将收到来自服务器的应答
本机cmd窗口
服务器程序运行窗口如下
服务器运行窗口

2.4 简单的UDP服务器的编写及测试

从客户端角度来看,使用UDP比TCP困难,因为客户端必须要注意丢失信息的问题。而另一方面,在服务器端使用UDP则要容易得多。我们在编写UDP服务器的时候,不用考虑丢失信息包的问题。毕竟,如果客户端来的信息包一直没到达的话,UDP服务器根本就不会知道曾经有客户端试图发送过请求。

关于如何在服务器端使用UDP,我们可以像使用TCP那样建立一个socket对象,设置选项。但由于UDP是无连接的协议,我们不必使用listen()或accept(),我们使用的是recvfrom()方法。recvfrom()方法类似于accept(),是一个阻塞方法,会阻塞程序执行,直到接收到数据为止。该方法返回接收到的数据和发送数据的源地址(客户端地址,包括IP和端口号)

以下是一个简单的UDP服务器的程序实现:

import socket

host = ''
port = 12345
# 创建UDP socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定指定地址和端口
server_socket.bind((host, port))
print(f'Server started on {host}:{port}')
while True:
    # 接收来自客户端的消息和地址
    data, client_addr = server_socket.recvfrom(1024)
    print(f'Received message "{data.decode('utf-8')}" from {client_addr}')
    # 发送响应消息给客户端
    server_socket.sendto(b'Hello,client!', client_addr)

下面介绍两种方法对UDP服务器进行测试(此处以Windows系统为例)

方法一 使用第三方工具Netcat(nc)进行测试

之前我们测试TCP服务器是用的Telnet指令,Telnet是一个基于文本的协议,通常用于与TCP服务器进行交互,它不适用于UDP协议,因为UDP协议是无连接的,无法像TCP那样建立持久的连接。

Netcat工具的安装及使用方法请读者自行上网查阅资料,此处不做过多说明。

  1. 运行服务器程序代码
    服务器端
  2. 安装好Netcat工具并配置好环境变量后,打开系统cmd窗口,输入指令 nc -u localhost 12345,回车,该指令向localhost(本机)的12345端口发送UDP数据包。接着在此窗口继续输入消息并回车即可向服务器端发送消息。此时也将收到来自UDP服务器的答复。
    cmd窗口

我们也可以使用管道操作符" | "将前一个命令的输出作为后一个命令的输入将两条指令合并成一条:echo Hello,Server! | nc -u localhost 12345
cmd

  1. 服务器收到数据并给出答复。
    服务端
    方法二 自定义UDP客户端脚本
    以下是一个简单的UDP客户端脚本示例:
import socket

host = 'localhost'
port = 12345
# 创建UDP socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 需要发送的消息
message = 'Hello,Server!'
# 发送消息到服务器
client_socket.sendto(message.encode('utf-8'), (host, port))
# 接收服务器响应
response, server_addr = client_socket.recvfrom(1024)
print('Received response:', response.decode('utf-8'))
client_socket.close()

我们首先运行服务器:服务器

然后直接运行客户端程序,我们将发送 “Hello,Server!” 消息到服务器,然后等待服务器的响应。
客户端
此时服务器将接收到客户端发送的消息并给出回复
服务端

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的小熊~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值