Python套接字消息交换

时间:2018-04-26 10:25:04

标签: python sockets

我必须在chatServer的python中编辑此代码,以便您可以将私人消息发送到接收方,如果接收方未登录,则会向发送方输出错误。此外,服务器发送给客户端的任何消息都必须提供原始发件人的名称。

chatClient.py

import socket
import struct
import sys
import threading
from datetime import datetime

PORT = 8888
HEADER_LENGTH = 2


def receive_fixed_length_msg(sock, msglen):
    message = b''
    while len(message) < msglen:
        chunk = sock.recv(msglen - len(message))
        if chunk == b'':
            raise RuntimeError("socket connection broken")
        message = message + chunk
    return message


def receive_message(sock):
    header = receive_fixed_length_msg(sock, HEADER_LENGTH)
    message_length = struct.unpack("!H", header)[0] 

    message = None
    if message_length > 0: 
        message = receive_fixed_length_msg(sock, message_length) 
        message = message.decode("utf-8")

    return message


def send_message(sock, message):
    encoded_message = message.encode("utf-8") 
    header = struct.pack("!H", len(encoded_message))

    message = header + encoded_message 
    sock.sendall(message);

def message_receiver():
    while True:
        msg_received = receive_message(sock)
        if len(msg_received) > 0:  # ce obstaja sporocilo
            current_time = datetime.now().strftime('%H:%M:%S')
            print("[RKchat][" + current_time + "] " + name + ": " + msg_received)

print("[system] connecting to chat server ...")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("localhost", PORT))
print("[system] connected!")

thread = threading.Thread(target=message_receiver)
thread.daemon = True
thread.start()

name = input("Vpiši ime: ")

while True:
    try:
        msg_send = input("")
        send_message(sock, msg_send)
    except KeyboardInterrupt:
        sys.exit()

chatServer.py

import signal

signal.signal(signal.SIGINT, signal.SIG_DFL)
import socket
import struct
import threading

PORT = 8888
HEADER_LENGTH = 2


def receive_fixed_length_msg(sock, msglen):
    message = b''
    while len(message) < msglen:
        chunk = sock.recv(msglen - len(message)) 
        if chunk == b'':
            raise RuntimeError("socket connection broken")
        message = message + chunk  # pripni prebrane bajte sporocilu

    return message


def receive_message(sock):
    header = receive_fixed_length_msg(sock, HEADER_LENGTH)
    message_length = struct.unpack("!H", header)[0]

    message = None
    if message_length > 0:
        message = receive_fixed_length_msg(sock, message_length)
        message = message.decode("utf-8")

    return message


def send_message(sock, message):
    encoded_message = message.encode("utf-8")

    header = struct.pack("!H", len(encoded_message))

    message = header + encoded_message
    sock.sendall(message);

def client_thread(client_sock, client_addr):
    global clients

    print("[system] connected with " + client_addr[0] + ":" + str(client_addr[1]))
    print("[system] we now have " + str(len(clients)) + " clients")

    try:
        while True:
            msg_received = receive_message(client_sock)

            if not msg_received: 
                break

            print("[RKchat] [" + client_addr[0] + ":" + str(client_addr[1]) + "] : " + msg_received)

            for client in clients:
                send_message(client, msg_received.upper())
    except:
        pass

    with clients_lock:
        clients.remove(client_sock)
    print("[system] we now have " + str(len(clients)) + " clients")
    client_sock.close()

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("localhost", PORT))
server_socket.listen(1)

print("[system] listening ...")
clients = set()
clients_lock = threading.Lock()
while True:
    try:
        client_sock, client_addr = server_socket.accept()
        with clients_lock:
            clients.add(client_sock)

        thread = threading.Thread(target=client_thread, args=(client_sock, client_addr));
        thread.daemon = True
        thread.start()

    except KeyboardInterrupt:
        break

print("[system] closing server socket ...")
server_socket.close()

1 个答案:

答案 0 :(得分:0)

您现在有一个简单的“接收消息,将其广播到所有”类型的客户端和服务器。正如在评论中所写,我们不会为您编写代码,但这里有一些您可以使用的想法。

首先,您需要构建协议。现在,您将服务器接收的所有内容视为消息,并将其广播到其他连接。如果您现在需要满足公共(向所有人广播)和私人消息(仅向特定用户发送),则服务器需要知道用户名。服务器还需要区分公共和私人消息。

例如,您可以在服务器中要求从新连接收到的第一条消息是

REGISTER username

然后您将识别此消息,而不是为客户端设置一组,您可以将其更改为字典并将用户名作为密钥

clients[username] = client_sock

或类似的东西。

当您的客户端线程收到消息时,它们也会解析它。可能的消息可能是

PUBLICMESSAGE message
PRIVATEMESSAGE username message

如果您收到公开信息,您可以按照自己现在的方式行事。如果您收到私人消息,您将获得套接字在哪里发送

try:
     client = clients[username]
     send_message(client, message)
except KeyError:
     Send an error message back stating the target user is not online

这些只是指针。您需要一个协议,因为您需要区分公共和私人消息,并且您需要让服务器知道您的用户名是什么,以便它可以将其用作标识符。然后,您需要构建一些解析来将协议关键字与消息分开。

我还想指出你之前写的一个回答。 Python: Multithreaded socket server runs endlessly when client stops unexpectedly请阅读从“TCP套接字传输数据而非消息”开始的最后几段。现在你的服务器不适合3-5场景,但它应该。当你建立一个协议,你会出问题在许多方面,如果您接到插座比你预期的,因为你会被吞咽连续协议关键字这变得更加重要。