Python Socket Message Exchange - PullRequest
       4

Python Socket Message Exchange

0 голосов
/ 26 апреля 2018

Мне нужно отредактировать этот код, который у меня есть на python сервера chatServer, чтобы вы могли отправлять личные сообщения получателю, и, если получатель не вошел в систему, выдает ошибку отправителю. Также любое сообщение, отправляемое сервером клиенту, должно содержать имя исходного отправителя.

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 голосов
/ 26 апреля 2018

Теперь у вас есть простое «получить сообщение, передать его всем» типа клиента и сервера. Как написано в комментариях, мы не пишем код для вас, но вот пара идей, которые вы могли бы использовать.

Во-первых, вам нужно создать протокол. Теперь вы рассматриваете все, что сервер получает как сообщение, и транслируете его на другие соединения Если вам теперь нужно обслуживать публичные (широковещательные сообщения для всех) и личные сообщения (отправлять только определенному пользователю), сервер должен знать имена пользователей. Сервер должен также различать публичные и личные сообщения.

Например, на вашем сервере может потребоваться, чтобы первое сообщение, полученное по новому соединению, было

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: многопоточный сервер сокетов работает бесконечно, когда клиент неожиданно останавливается Пожалуйста, прочитайте последние параграфы, начиная с "TCP-сокеты передают данные, а не сообщения". Теперь ваш сервер не обслуживает сценарии 3-5, но он должен. Это становится более важным, когда вы создаете протокол, так как вы будете ошибаться во многих отношениях, если вы получите от сокета больше, чем вы предполагали, как если бы вы глотали последовательные ключевые слова протокола.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...