一. 对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数据库的数据。