python socket 实时通信,多对多,一对一,转发

研究一下python socket 实时通信,多对多,一对一,转发

C=>S 单独通信

server1

import socket
import threading

# 在线客户端列表
online_clients = {}

def broadcast(message, sender):
    """向所有在线客户端广播消息,除了发送者"""
    for client_socket, addr in online_clients.items():
        if client_socket != sender:
            try:
                client_socket.send(message)
            except:
                # 客户端离线,从列表中移除
                online_clients.pop(client_socket)

def handle_client(client_socket, addr):
    """处理单个客户端的通信"""
    while True:
        try:
            data = client_socket.recv(1024)
            if not data:
                break
            print(f"从 {addr} 收到消息: {data.decode()}")
            broadcast(data, client_socket)
        except:
            break

    # 客户端离线,从列表中移除
    online_clients.pop(client_socket)
    client_socket.close()
    print(f"客户端 {addr} 已离线")

# 启动服务器
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("localhost", 8000))
server_socket.listen(5)
print("服务器已启动,等待客户端连接...")

while True:
    client_socket, addr = server_socket.accept()
    print(f"新客户端 {addr} 已连接")
    online_clients[client_socket] = addr
    threading.Thread(target=handle_client, args=(client_socket, addr)).start()

client1

import socket

# 创建 Socket 对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接服务器
server_address = ("localhost", 8000)
client_socket.connect(server_address)

while True:
    message = input("输入消息: ")
    client_socket.sendall(message.encode())

    # 接收服务器响应
    data = client_socket.recv(1024)
    print(f"接收服务器响应收到消息: {data.decode()}")

client_socket.close()

效果:
在这里插入图片描述

C<=>S 相互通信

server2

import socket
import select

# 在线客户端列表
online_clients = {}

def broadcast(message, sender):
    """向所有在线客户端广播消息,除了发送者"""
    for client_socket, addr in online_clients.items():
        if client_socket != sender:
            try:
                client_socket.sendall(message)
            except:
                # 客户端离线,从列表中移除
                online_clients.pop(client_socket)

def server_loop():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(("localhost", 8000))
    server_socket.listen(5)
    print("服务器已启动,等待客户端连接...")

    # 使用 select 监听 socket 列表
    inputs = [server_socket]

    while True:
        # 阻塞等待至少一个socket准备好进行I/O操作
        readable, writeable, exceptional = select.select(inputs, [], [])

        for sock in readable:
            if sock is server_socket:
                # 新的连接请求
                client_socket, addr = server_socket.accept()
                print(f"新客户端 {addr} 已连接")
                online_clients[client_socket] = addr
                inputs.append(client_socket)
            else:
                # 已连接客户端发来消息
                try:
                    data = sock.recv(1024)
                    if data:
                        message = f"客户端 {online_clients[sock]}: {data.decode()}"
                        print(message)
                        broadcast(message.encode(), sock)
                    else:
                        # 客户端离线,从列表中移除
                        online_clients.pop(sock)
                        inputs.remove(sock)
                        sock.close()
                        print(f"客户端 {online_clients[sock]} 已离线")
                except:
                    # 客户端异常,从列表中移除
                    online_clients.pop(sock)
                    inputs.remove(sock)
                    sock.close()
                    print(f"客户端 {online_clients[sock]} 已离线")

if __name__ == "__main__":
    server_loop()

client2

import socket
import threading

def send_message(client_socket):
    while True:
        message = input()
        client_socket.sendall(message.encode())

def receive_message(client_socket):
    while True:
        try:
            data = client_socket.recv(1024)
            if data:
                print(data.decode())
            else:
                # 服务器断开连接
                break
        except:
            break

if __name__ == "__main__":
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = ("localhost", 8000)
    client_socket.connect(server_address)
    print("已连接到服务器")

    threading.Thread(target=send_message, args=(client_socket,)).start()
    threading.Thread(target=receive_message, args=(client_socket,)).start()

效果:
在这里插入图片描述

C<->S 固定端口一一对应

server3

import socket
import select
from collections import defaultdict

# 客户端端口号映射
client_ports = {
    50106: "A",
    50107: "B",
    50108: "C"
}

# 允许通信的端口号对
allowed_ports = {
    50106: [50108],
    50107: [50109],
    50108: [50106]
}

# 在线客户端列表,{端口号: socket}
online_clients = {}
# 消息队列,{端口号: [(发送者端口号, 消息)]}
message_queues = defaultdict(list)

def send_message(sender_port, target_port, message):
    """向指定端口号的客户端发送消息"""
    target_socket = online_clients.get(target_port)
    if target_socket:
        try:
            target_socket.sendall(f"{sender_port}: {message}".encode())
        except:
            # 接收方离线,将消息加入队列
            message_queues[target_port].append((sender_port, message))
    else:
        # 目标端口号不存在,将消息加入队列
        message_queues[target_port].append((sender_port, message))

def server_loop():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(("localhost", 8000))
    server_socket.listen(5)
    print("服务器已启动,等待客户端连接...")

    # 使用 select 监听 socket 列表
    inputs = [server_socket]

    while True:
        # 阻塞等待至少一个socket准备好进行I/O操作
        readable, writeable, exceptional = select.select(inputs, [], [])

        for sock in readable:
            if sock is server_socket: 
                # 新的连接请求
                client_socket, addr = server_socket.accept()
                print(f"新客户端 {addr} 已连接")
                # client_port = client_socket.getsockname()[1]
                client_port = addr[1] 
                online_clients[client_port] = client_socket
                inputs.append(client_socket)
                # 将离线期间的消息队列发送给新上线客户端
                for sender_port, message in message_queues.pop(client_port, []):
                    client_socket.sendall(f"{sender_port}: {message}".encode())
            else:
                # 已连接客户端发来消息
                try:
                    data = sock.recv(1024)
                    if data:
                        print(f"接收到消息: {data} ")
                        # 解析消息,格式为 "目标端口号:消息内容"
                        parts = data.decode().split(':')  
  
                        # 确保拆分后的部分数量正确  
                        if len(parts) == 3:  
                            # 转换第一个部分为整数,作为sender_port  
                            sender_port = int(parts[0])
                            # 转换第二个部分为整数,作为target_port    
                            target_port = int(parts[1])  
                             # 第三个部分作为message
                            message = parts[2]          
 
                        # 检查是否允许通信
                        if target_port in allowed_ports.get(sender_port, []):
                            send_message(sender_port, target_port, message)
                        else:
                            print(f"客户端 {client_ports.get(sender_port, sender_port)} 试图与未授权端口 {target_port} 通信")
                    else:
                        # 客户端离线,从列表中移除
                        client_port = sock.getsockname()[1]
                        online_clients.pop(client_port)
                        inputs.remove(sock)
                        sock.close()
                        print(f"客户端 {client_ports.get(client_port, client_port)} 已离线")
                except: 
                    try:  
                        # 获取远程客户端的地址和端口号  
                        client_address, client_port = sock.getpeername()  
                        print(f"客户端 {client_address}:{client_port} 报错离线离线")
                        # 根据需要处理client_port,例如从列表中移除  
                        online_clients.pop(client_port, None)  # 使用pop的第二个参数避免KeyError  
                        inputs.remove(sock)  
                        sock.close()  
                        print(f"客户端 {client_address}:{client_port} 已离线")
                    except Exception as e:  
                        # 处理可能出现的异常,例如socket已经关闭等  
                        print(f"客户端强制离线Error handling client socket: {e}") 
 
if __name__ == "__main__":
    server_loop()

client3-1

import socket
import threading

def send_message(client_socket,sender_port, target_port):
    while True:
        message = input()
        if message.strip() == "exit":
            break
        client_socket.sendall(f"{sender_port}:{target_port}:{message}".encode())

def receive_message(client_socket):
    while True:
        try:
            data = client_socket.recv(1024)
            if data:
                print(data.decode())
            else:
                # 服务器断开连接
                break
        except:
            break

if __name__ == "__main__":
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = ("localhost", 8000)
    sender_port = 50106
    client_socket.bind(("localhost", sender_port))  # 绑定固定端口号 50108
    client_socket.connect(server_address)
    client_port = client_socket.getsockname()[1]
    print(f"已连接到服务器,本地端口号为 {client_port}")

    # 客户端 A  只能与端口号 50106 的客户端  C通信
    target_port = 50108

    sender_thread = threading.Thread(target=send_message, args=(client_socket, sender_port,target_port))
    receiver_thread = threading.Thread(target=receive_message, args=(client_socket,))
    sender_thread.start()
    receiver_thread.start()
    sender_thread.join()
    client_socket.close()

client3-2

import socket
import threading

def send_message(client_socket,sender_port, target_port):
    while True:
        message = input()
        if message.strip() == "exit":
            break
        client_socket.sendall(f"{sender_port}:{target_port}:{message}".encode())

def receive_message(client_socket):
    while True:
        try:
            data = client_socket.recv(1024)
            if data:
                print(data.decode())
            else:
                # 服务器断开连接
                break
        except:
            break

if __name__ == "__main__":
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = ("localhost", 8000)
    sender_port = 50107
    client_socket.bind(("localhost", sender_port))  # 绑定固定端口号 50108
    client_socket.connect(server_address)
    client_port = client_socket.getsockname()[1]
    print(f"已连接到服务器,本地端口号为 {client_port}")

    # 客户端 A  只能与端口号 50106 的客户端  C通信
    target_port = 50109

    sender_thread = threading.Thread(target=send_message, args=(client_socket, sender_port,target_port))
    receiver_thread = threading.Thread(target=receive_message, args=(client_socket,))
    sender_thread.start()
    receiver_thread.start()
    sender_thread.join()
    client_socket.close()

client3-3

import socket
import threading

def send_message(client_socket,sender_port, target_port):
    while True:
        message = input()
        if message.strip() == "exit":
            break
        client_socket.sendall(f"{sender_port}:{target_port}:{message}".encode())

def receive_message(client_socket):
    while True:
        try:
            data = client_socket.recv(1024)
            if data:
                print(data.decode())
            else:
                # 服务器断开连接
                break
        except:
            break

if __name__ == "__main__":
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = ("localhost", 8000)
    sender_port = 50108
    client_socket.bind(("localhost", sender_port))  # 绑定固定端口号 50108
    client_socket.connect(server_address)
    client_port = client_socket.getsockname()[1]
    print(f"已连接到服务器,本地端口号为 {client_port}")

    # 客户端 C 只能与端口号 50106 的客户端 A 通信
    target_port = 50106

    sender_thread = threading.Thread(target=send_message, args=(client_socket, sender_port,target_port))
    receiver_thread = threading.Thread(target=receive_message, args=(client_socket,))
    sender_thread.start()
    receiver_thread.start()
    sender_thread.join()
    client_socket.close()

C<-S-S->C 固定端口一一对应

local

import socket
import threading

# TCP服务器地址和端口
SERVER1_ADDRESS = ('localhost', 8001)
SERVER2_ADDRESS = ('localhost', 8002)

# 用于从一个socket接收数据并发送到另一个socket的函数
def forward_data(source_socket, dest_socket):
    while True:
        try:
            data = source_socket.recv(1024)
            if data:
                dest_socket.sendall(data)
        except:
            break

# 连接到8001端口的函数
def connect_to_server1():
    while True:
        try:
            server1_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            server1_socket.connect(SERVER1_ADDRESS)
            print("已连接到8001端口")
            return server1_socket
        except:
            print("连接8001端口失败,正在重试...")
            continue

# 连接到8002端口的函数
def connect_to_server2():
    while True:
        try:
            server2_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            server2_socket.connect(SERVER2_ADDRESS)
            print("已连接到8002端口")
            return server2_socket
        except:
            print("连接8002端口失败,正在重试...")
            continue

# 连接到8001端口
server1_socket = connect_to_server1()

# 连接到8002端口
server2_socket = connect_to_server2()

# 创建两个线程,分别用于数据转发
thread1 = threading.Thread(target=forward_data, args=(server1_socket, server2_socket))
thread2 = threading.Thread(target=forward_data, args=(server2_socket, server1_socket))

# 启动线程
thread1.start()
thread2.start()

# 等待线程结束
thread1.join()
thread2.join()

server

import socket
import threading
from collections import defaultdict

# 存储每个端口连接的客户端,使用defaultdict初始化为列表
clients = defaultdict(list)

def handle_client(client_socket, client_address, port):
    while True:
        try:
            data = client_socket.recv(1024)
            if data:
                # 将数据转发给另一个端口的所有客户端
                other_port = 8000 if port == 8001 else 8001
                for other_client in clients[other_port]:
                    other_client.send(data)
            else:
                # 如果没有数据,关闭连接
                break
        except Exception as e:
            print(f"Error handling data from {client_address}: {e}")
            break

    try:
        # 从客户端列表中移除断开连接的客户端
        clients[port].remove(client_socket)
        client_socket.close()
        print(f"Closed connection to {client_address}")
    except Exception as e:
        print(f"Closed connection to  {client_address}:{port} => {e}")
     

def accept_connections(port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
        server_socket.bind(('', port))
        server_socket.listen()
        print(f"Listening on port {port}")

        while True:
            try:
                client_socket, client_address = server_socket.accept()
                print(f"Accepted connection from {client_address} on port {port}")
                clients[port].append(client_socket)
                # 创建一个新线程来处理客户端
                threading.Thread(target=handle_client, args=(client_socket, client_address, port)).start()
            except RuntimeError as e:
                print(f"创建一个新线程来处理客户端 {port} => {e}")

# 创建并启动两个线程,分别监听8000和8001端口
thread1 = threading.Thread(target=accept_connections, args=(8000,))
thread2 = threading.Thread(target=accept_connections, args=(8001,)) 

thread1.start()
thread2.start()

thread1.join()
thread2.join()

local_server

import socket
import threading

# 创建一个TCP/IP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)


# 绑定IP地址和端口
server_address = ('', 8002)
server_socket.bind(server_address)

# 开始监听连接
server_socket.listen(5)
print("等待连接[8002]...")

# 客户端列表
clients = []

# 处理客户端连接的函数


def handle_client(client_socket, address):
    print(f"已连接 {address}")
    clients.append(client_socket)

    while True:
        try:
            # 接收客户端消息
            data = client_socket.recv(1024)
            if not data:
                break
            # 打印接收到的消息
            print(f"来自 {address} 的消息: {data.decode()}")
        except:
            print(f"与 {address} 的连接已断开")
            clients.remove(client_socket)
            client_socket.close()
            break


try:
    # 等待连接并创建新的线程来处理每个连接
    while True:
        client_socket, address = server_socket.accept()
        client_handler = threading.Thread(
            target=handle_client, args=(client_socket, address))
        client_handler.start()

        # 控制台输入消息并发送给所有客户端

        while True:
            message = input("请输入消息: ")
            for client in clients:
                client.sendall(message.encode())
except KeyboardInterrupt:
    print("收到 Ctrl+C,取消端口绑定...")
    server_socket.close()

bot

import socket
import threading

def receive_data(sock):
    while True:
        try:
            data = sock.recv(1024)
            if not data:
                break
            print(f"Received: {data.decode('utf-8')}")
        except Exception as e:
            print(f"Error receiving data: {e}")
            break

def main():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
        try:
            # 连接到服务器
            client_socket.connect(('127.0.0.1', 8000))
            print("Connected to server on port 8000.")

            # 创建一个线程用于接收数据
            threading.Thread(target=receive_data, args=(client_socket,)).start()

            while True:
                # 从控制台读取数据
                message = input()
                if message:
                    client_socket.sendall(message.encode('utf-8'))
        except Exception as e:
            print(f"Connection error: {e}")

if __name__ == "__main__":
    main()

效果:
在这里插入图片描述

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Python Socket UDP通信是一种基于UDP协议的网络通信方式,它可以实现点对点的数据传输,具有高效、快速、简单等优点。在Python中,我们可以使用socket模块来实现UDP通信,通过创建socket对象、绑定IP地址和端口号、发送和接收数据等操作,实现网络通信。同时,Python Socket UDP通信也可以应用于各种场景,如网络游戏、实时通信、数据采集等。 ### 回答2: Python socket库是Python标准库的一部分,用于实现网络通信。其使用非常简单,主要是调用socket模块下的一些方法即可完成常见网络通信任务。在Python中,UDP通信是基于socket进行实现的。 UDP是一种无连接协议,通信双方之间没有建立可靠的连接。虽然UDP不如TCP那样可靠,但是它的实时性好,适合于需要实时传输数据的场景。UDP的通信方式是通过数据报进行传输,每个数据报最大长度为65,535个字节。 在Python中,通过socket模块创建UDP套接字(socket),然后通过该套接字实现UDP通信。创建UDP套接字的方法为: ``` import socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ``` 其中,`AF_INET`代表IPv4协议,`SOCK_DGRAM`代表使用UDP数据报协议。 接下来,就可以使用该套接字实现UDP通信了。 在进行UDP通信时,需要注意以下几点: 1. 给套接字绑定IP地址和端口号 在Python中,使用`bind()`方法将IP地址和端口号绑定到套接字上,以便可以接收来自这个地址和端口的消息。例如: ``` server_socket.bind(('127.0.0.1', 9999)) ``` 其中,`127.0.0.1`表示本机IP地址,`9999`表示端口号。 2. 发送数据报 通过套接字的`sendto()`方法向指定的IP地址和端口号发送数据报。例如: ``` server_socket.sendto('hello, world!', ('127.0.0.1', 8888)) ``` 其中,`'hello, world!'`表示需要发送的数据,`('127.0.0.1', 8888)`表示目标IP地址和端口号。 3. 接收数据报 通过套接字的`recvfrom()`方法接收来自指定IP地址和端口号的数据报。例如: ``` data, addr = server_socket.recvfrom(1024) ``` 其中,`1024`表示接收数据的缓冲区大小。 4. 关闭套接字 在UDP通信完成后,需要使用`close()`方法关闭套接字。例如: ``` server_socket.close() ``` 以上就是Python socket库进行UDP通信的基本步骤。值得注意的是,在实际的网络通信中可能会遇到各种异常情况,例如网络故障、套接字参数错误等,需要合理的使用异常处理机制,以保证程序的正确性和稳定性。 ### 回答3: Python Socket UDP通信是指使用socket库中的UDP协议进行数据传输的一种通信方式。UDP(User Datagram Protocol,用户数据报协议)是在IP协议的基础上实现的一种无连接的传输层协议。UDP协议提供了无状态的、不可靠的数据传输服务,可以快速传输数据包,但是不能提供可靠的数据传输服务,因此在网络上的应用较少,通常用于需要快速传输而不需要可靠性保证的应用场景。 Python中可以通过使用socket库中的socket类来进行UDP通信。在使用socket进行UDP通信时,需要指定socket的类型为SOCK_DGRAM(表示UDP协议),同时需要指定目标地址和端口号。发送数据时,使用sendto()方法将数据打包发送到指定的目标地址和端口号。接收数据时,使用recvfrom()方法接收数据,并返回发送方的地址和端口号。 下面是一个简单的Python Socket UDP通信的示例代码: ```python import socket # 创建UDP socket udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 目标地址和端口号 target_addr = ('127.0.0.1', 8888) # 发送数据 data = 'Hello, UDP!' udp_socket.sendto(data.encode(), target_addr) # 接收数据 recv_data, addr = udp_socket.recvfrom(1024) print('Received from {}: {}'.format(addr, recv_data.decode())) # 关闭socket udp_socket.close() ``` 在这个示例代码中,首先创建了一个UDP socket,指定了目标地址和端口号为127.0.0.1和8888。然后使用sendto()方法将字符串“Hello, UDP!”发送到目标地址和端口号。接着使用recvfrom()方法接收数据,返回发送方的地址和端口号,将接收到的数据打印出来。最后关闭socket。 需要注意的是,在UDP通信中,由于UDP协议的不可靠性,发送方无法确定数据是否被接收方接收到。如果需要可靠的数据传输服务,应该使用TCP协议进行通信

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值