客户端通过服务器进行TCP通信(三)

发布于:2024-07-16 ⋅ 阅读:(69) ⋅ 点赞:(0)

一. 对TCP的基础讲解

服务端

1. 首先创建一个套接字,TCP是面向字节流的套接字,故需要使用SOCK_STREAM

2. 然后使用bind()函数将套接字与服务器地址关联(如果是在本地测试,直接将地址设置为217.0.0.1或者localhost,端口号为10000)

3. listen()将套接字设置为监听状态

3. 等待,参数为最大排队数

4. 在循环中,调用accept()等待客户端的消息连接,如果有客户端进行连接,那么accept()函数会返回一个打开的连接与客户端地址

6. 指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据

7. 通过sendall()进行回传客户端数据

8. 传回数据后,与当前的客户端通信就算完成了。需要使用close()进行关闭清理

客户端

1. 创建一个套接字

2. 使用connect()函数连接到服务器

3. 通过sendall()向服务器发送数据

4. 通过recv()接受服务器传递回的数据

5. 交互完成之后,使用close()关闭清理

二. 编写Python程序

2.1 Python实现一个服务端和一个客户端连接

服务端代码

# coding=gb2312
import socket
 
socket_server = socket.socket()
socket_server.bind(("localhost", 8888))
# 监听端口
socket_server.listen(1)
# 等待客户端连接,accept方法返回二元元组(连接对象, 客户端地址信息)
print(f"服务端已开始监听,正在等待客户端连接...")
conn, address = socket_server.accept()
print(f"接收到了客户端的连接,客户端的信息:{address}")
 
# 接受客户端信息,使用客户端和服务端的本次连接对象,而非socket_server
while True:
    # 接收消息
    data: str = conn.recv(1024).decode("UTF-8")
    print(f"客户端发来的消息是:{data}")
    # 回复消息
    msg = input("请输入你要回复客户端的消息:")
    if msg == 'exit':
        break
    conn.send(msg.encode("UTF-8"))  # encode将字符串编码为字节数组对象
 
# 关闭连接
conn.close()
socket_server.close()

客户端代码

# coding=gb2312
import socket
# 创建socket对象
socket_client = socket.socket()
# 连接到服务器
socket_client.connect(("localhost", 8888))
 
while True:
    send_msg = input("请输入要发送给服务端的消息:")
    if send_msg == "exit":
        break
    # 发送消息
    socket_client.send(send_msg.encode("UTF-8"))
    # 接受消息
    recv_data = socket_client.recv(1024).decode("UTF-8")    # 1024是缓冲区大小,一般就填1024, recv是阻塞式
    print(f"服务端回复的消息是:{recv_data}")
 
# 关闭连接
socket_client.close()

运行之后我们就实现了客户端和服务端的通信,但此时只能有一个客户端连接到服务端,如果想实现多个客户端,就需要修改服务端listen()的参数,允许更多的客户端连接,下面进行讲解。

2.2 Python实现一个服务端和多个客户端连接

服务端代码

# coding=gb2312
import socket
import threading
 
 
def create_server_socket(host, port):
    socket_server = socket.socket()
    socket_server.bind((host, port))
    socket_server.listen(5)
    print(f"服务端已启动,地址{host},端口{port}")
    print(f"正在等待客户端连接...")
    # 开启多线程,收发来自多个客户端的数据
    num = 0     # 标记客户端的编号
    while True:
        num += 1
        conn, address = socket_server.accept()
        print(f"服务端已接受到客户端 {num}号 的连接请求,客户端信息:{address}")
        client_handler = threading.Thread(target=handle_client, args=(conn, address, num))
        client_handler.start()
 
 
# 处理收发数据
def handle_client(conn, address, num):
    while True:
        # 接收客户端发来的数据
        data_from_client: str = conn.recv(1024).decode("UTF-8")
        print(f"客户端 {num}号:{address}发来的消息是:{data_from_client}")
        # 发送消息到客户端
        msg = input(f"请输入你要回复客户端 {num}号:{address}的消息:")
        if msg == 'exit':
            break
        conn.send(msg.encode("UTF-8"))  # encode将字符串编码为字节数组对象
    conn.close()
 
 
if __name__ == '__main__':
    server_host = input("请输入服务端Host:")
    server_port = int(input("请输入服务端port:"))
    create_server_socket(server_host, server_port)

客户端代码

# coding=gb2312
import socket
 
 
def create_client(host, port):
    socket_client = socket.socket()
    socket_client.connect((host, port))
    # 发送、接受数据
    while True:
        msg = input(f"请输入客户端1发送给服务端{host}:{port}的数据:")
        if msg == "exit":
            break
        # 发送数据到服务端
        socket_client.send(msg.encode("UTF-8"))
        # 接收服务端的数据
        data_from_server = socket_client.recv(1024).decode("UTF-8")
        print(f"客户端接收到服务端的消息:{data_from_server}")
    socket_client.close()
 
 
if __name__ == '__main__':
    server_host = input("请输入想要连接的服务端Host:")
    server_port = int(input("请输入想要连接的服务端port:"))
    create_client(server_host, server_port)

三. 移植Python程序

注意,服务器上的python版本和vscode上使用的服务器版本一定要一致。因为上述代码都是在Python3下写的,且我服务器的的python版本是python2,所以下面对代码进行一定的更改。

# coding=gb2312
# -*- coding: utf-8 -*-
# import os
# os.environ["CUDA_VISIBLE_DEVICES"] = "0, 1"
from __future__ import print_function
import socket
import threading

 
def create_server_socket():
    socket_server = socket.socket()
    socket_server.bind(("localhost",50000))
    # socket_server.bind(("47.116.118.86",5000))
    socket_server.listen(65000)
    print("正在等待客户端连接...")
    # 开启多线程,收发来自多个客户端的数据
    num = 0     # 标记客户端的编号
    while True:
        num += 1
        conn, address = socket_server.accept()
        print("服务端已接受到客户端 {}号 的连接请求,客户端信息:{}".format(num,address))
        client_handler = threading.Thread(target=handle_client, args=(conn, address, num))

        client_handler.start()
 
 
# 处理收发数据
def handle_client(conn, address, num):
    while True:
        # 接收客户端发来的数据
        data_from_client = conn.recv(1024).decode("UTF-8")
        print("客户端 {}号:{}发来的消息是:{}".format(num,address,data_from_client))
        # 发送消息到客户端
        #msg = raw_input("请输入你要回复客户端 {}号:{}的消息:".format(num,address))
        msg = data_from_client
        if msg == 'exit':
            break
        conn.send(msg.encode("UTF-8"))  # encode将字符串编码为字节数组对象
    conn.close()
 
 
if __name__ == '__main__':
    create_server_socket();
    # create_server_socket("172.19.84.229", 8080)
    # create_server_socket("", 8080)

3.2 在服务器运行Python程序

在py文件中加入以下代码:

import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0, 1"

添加后,在服务器直接运行python xxxx.py,就可以运行这个Python文件了。

四. 实现服务端运行python服务端程序,与客户端通信

在阿里云服务器监听服务器的内网IP,例如172.22.71.222,端口号设置为8080.

在客户端去连接的不是服务器的内网IP,而是云服务器的公网IP,如47.112.113.43,端口号也设置为8080

这样便能连接成功

这样我们就可以在云服务器作为服务端,在自己本机作为客户端,实现TCP传输。

如果我们要实现客户端到客户端的通信的话,可以在服务端开辟多个线程,去处理客户端的数据。

此时主线程负责对发起请求的客户创建链接,并且将每个用户对应的链接保存到一个字典中去,方便调用。对于每个用户链接,都创建两个子线程,一个子线程用来发送数据,另外一个子线程用来接收数据。

但这种方法如果客户端多的话,就对资源消耗很大,因此这个项目我就摒弃了这个方法,从而采用直接从数据库中获取数据,实现多个客户端去获取云服务器的MYSQL数据库的数据。