网络编程案例多任务版TCP服务端程序开发

Posted ZSYL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络编程案例多任务版TCP服务端程序开发相关的知识,希望对你有一定的参考价值。

案例-多任务版TCP服务端程序开发

学习目标

  • 能够说出多任务版TCP服务端程序的实现过程

1. 需求

目前我们开发的TCP服务端程序只能服务于一个客户端,如何开发一个多任务版的TCP服务端程序能够服务于多个客户端呢?

完成多任务,可以使用 线程,比进程更加节省内存资源。

2. 具体实现步骤

  1. 编写一个TCP服务端程序,循环等待接受客户端的连接请求
  2. 当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞
  3. 把创建的子线程设置成为守护主线程,防止主线程无法退出。

3. 多任务版TCP服务端程序的示例代码

import socket
import threading


# 处理客户端请求的任务
def handle_client_request(ip_port, new_client):
    print("客户端的ip和端口号为:", ip_port)
    # 5. 接收客户端的数据
    # 收发消息都使用返回的这个新的套接字
    # 循环接收客户端的消息
    while True:
        recv_data = new_client.recv(1024)
        if recv_data:
            print("接收的数据长度是:", len(recv_data))
            # 对二进制数据进行解码变成字符串
            recv_content = recv_data.decode("gbk")
            print("接收客户端的数据为:", recv_content, ip_port)

            send_content = "问题正在处理中..."
            # 对字符串进行编码
            send_data = send_content.encode("gbk")
            # 6. 发送数据到客户端
            new_client.send(send_data)
        else:
            # 客户端关闭连接
            print("客户端下线了:", ip_port)
            break
    # 关闭服务与客户端套接字,表示和客户端终止通信
    new_client.close()


if __name__ == '__main__':

    # 1. 创建tcp服务端套接字
    # AF_INET: ipv4 , AF_INET6: ipv6
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置端口号复用,表示意思: 服务端程序退出端口号立即释放
    # 1. SOL_SOCKET: 表示当前套接字
    # 2. SO_REUSEADDR: 表示复用端口号的选项
    # 3. True: 确定复用
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 2. 绑定端口号
    # 第一个参数表示ip地址,一般不用指定,表示本机的任何一个ip即可
    # 第二个参数表示端口号
    tcp_server_socket.bind(("", 9090))
    # 3. 设置监听
    # 128: 表示最大等待建立连接的个数
    tcp_server_socket.listen(128)
    # 4. 等待接受客户端的连接请求
    # 注意点: 每次当客户端和服务端建立连接成功都会返回一个新的套接字
    # tcp_server_socket只负责等待接收客户端的连接请求,收发消息不使用该套接字
    # 循环等待接受客户端的连接请求
    while True:

        new_client, ip_port = tcp_server_socket.accept()
        # 代码执行到此,说明客户端和服务端建立连接成功
        # 当客户端和服务端建立连接成功,创建子线程,让子线程专门负责接收客户端的消息
        sub_thread = threading.Thread(target=handle_client_request, args=(ip_port, new_client))
        # 设置守护主线程,主线程退出子线程直接销毁
        sub_thread.setDaemon(True)
        # 启动子线程执行对应的任务
        sub_thread.start()

    # 7. 关闭服务端套接字, 表示服务端以后不再等待接受客户端的连接请求
    # tcp_server_socket.close()  # 因为服务端的程序需要一直运行,所以关闭服务端套接字的代码可以省略不写

执行结果:

客户端连接成功: ('172.16.47.209', 51528)
客户端连接成功: ('172.16.47.209', 51714)
hello1 ('172.16.47.209', 51528)
hello2 ('172.16.47.209', 51714)

4. 小结

  1. 编写一个TCP服务端程序,循环等待接受客户端的连接请求
while True:
	service_client_socket, ip_port = tcp_server_socket.accept()
  1. 当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞
 while True:
     service_client_socket, ip_port = tcp_server_socket.accept() 
     sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
     sub_thread.start()
  1. 把创建的子线程设置成为守护主线程,防止主线程无法退出。
 while True:
     service_client_socket, ip_port = tcp_server_socket.accept() 
     sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
     sub_thread.setDaemon(True) 
     sub_thread.start()

模拟QQ聊天-客户端

from socket import *
from threading import Thread

flag = True

def readMsg(client_socket):
    while flag:
        recv_data = client_socket.recv(1024)
        print('收到:', recv_data.decode('utf-8'))


def writeMsg(client_socket):
    global flag

    while flag:
        msg = input('>')
        msg = user_name+'说:'+msg

        client_socket.send(msg.encode('utf-8'))
        if msg.endswith('bye'):
            flag = False
            break


# 创建客户端套接字对象
client_socket = socket(AF_INET, SOCK_STREAM)
user_name = input('请输入用户名:')
# 调用connect连接服务器
client_socket.connect(('192.168.0.108', 8888))

# 开启一个线程处理客户端的读取消息
t1 = Thread(target=readMsg, args=(client_socket,))
t1.start()

# 开启一个线程处理客户端的发送消息
t2 = Thread(target=writeMsg, args=(client_socket,))
t2.start()

t1.join()
t2.join()

client_socket.close()

模拟QQ聊天-服务端

from socket import *
from threading import Thread

# 存储所有的sockets
sockets = []

def main():
    # 创建server_socket套接字对象
    server_socket = socket(AF_INET, SOCK_STREAM)

    # 绑定端口
    server_socket.bind(('', 8888))

    # 监听
    server_socket.listen()

    # 接收客户端的请求
    server_socket.accept()

    while True:
        client_socket, client_info = server_socket.accept()
        sockets.append(client_socket)

        # 开启线程处理当前客户端的请求
        t = Thread(target=readMsg, args=(client_socket,))
        t.start()

def readMsg(client_socket):
    # 读取客户端发送来的消息
    while True:
        recv_data = client_socket.recv(1024)

        # 如果接收的消息中结尾是bye,则在客户端列表移除
        if recv_data.decode('utf-8').endswidth('bye'):
            sockets.remove(client_socket)
            client_socket.close()
            break
            

        # 将消息发送给所有在线的客户端
        # 遍历所有在线客户端列表
        if len(recv_data) > 0:
            for socket in sockets:
                socket.send(recv_data)


if __name__ == '__main__':
    main()

加油!

感谢!

努力!

以上是关于网络编程案例多任务版TCP服务端程序开发的主要内容,如果未能解决你的问题,请参考以下文章

Python网络编程之多任务版TCP服务端程序开发

TCP案例代码

TCP案例代码

Python进阶:利用线程实现多任务

网络编程套接字

java网络多线程专题