I am facing an error where the server cannot send confirmation messages to the client in Python TCP socket

Viewed 72

I am trying to build a console chat app on Python socket. I currently running into some issues regarding the server sending message to the client. I managed to successfully send the request from the client to the server and printed it out. However, when I try to sent the confirmation message back to the client, there were always problems that I couldn't quite understand.

Here is my client side:

import socket
import select
import errno
import sys, struct


HEADER_LENGTH = 1024
IP = "127.0.0.1"
PORT = 9669


ProtoHeader = struct.Struct("!HI")

LoginRequest = 1
MessageRequest = 2
ConversationRequest = 3

def format_login_request(username):
    username_bytes = username.encode()
    proto_block = ProtoHeader.pack(LoginRequest, len(username_bytes)) + username_bytes
    return proto_block
   

def format_send_message(recv_id, message):
    message_bytes = message.encode()
    recv_id = recv_id.encode()
    proto_block = ProtoHeader.pack(MessageRequest, len(message_bytes)+len(recv_id)) + recv_id+message_bytes
    return proto_block

def format_con_request(conv_id):
    recv_id_bytes = recv_id.encode()
    proto_block = ProtoHeader.pack(ConversationRequest, len(recv_id_bytes)) + recv_id_bytes
    return proto_block
# Create a socket

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Connect to a given ip and port
client_socket.connect((IP, PORT))

client_socket.setblocking(False)
my_username = input("Username: ")
print(format_login_request(my_username))
client_socket.sendall(format_login_request(my_username))
username_conf = client_socket.recv(HEADER_LENGTH).decode()
print(username_conf)
if username_conf == "Welcome to the server":
    con_id = input("Please enter conversation's id, if don't have one, please enter no ")
    if con_id == 'no':
        
        client_socket.send(format_con_request(con_id))

    else: 
        client_socket.send(format_con_request(con_id))

    conversation = client_socket.recv(HEADER_LENGTH).decode()

    recv_id = input("Please enter receiver's id")

    while True:
            # Wait for user to input a message

            message = input(f'{my_username} > ')
            # If message is not empty - send it
            if message:

                # send_message = send_message(recv_id,message)
                client_socket.sendall(format_send_message(recv_id,message))

            try:

                while True:
                    message_receiver = client_socket.recv(HEADER_LENGTH).decode()
                    x = message_receiver.split('|')
                    print(x)
                    username = x[0]
                    message = x[1]
                    # Print message
                    print(f'{username} > {message}')

            except IOError as e:

                if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:
                    print('Reading error: {}'.format(str(e)))
                    sys.exit()

                # We just did not receive anything
                continue

            except Exception as e:
                # Any other exception - something happened, exit
                print('Reading error: {}'.format(str(e)))
                sys.exit()

Here is the console terminal for client:

Username: user1

b'\x00\x01\x00\x00\x00\x05user1'
Traceback (most recent call last):
  File "C:\Users\Duong Dang\Desktop\bai 2.3\client.py", line 57, in <module>
    username_conf = client_socket.recv(HEADER_LENGTH).decode()
BlockingIOError: [WinError 10035] A non-blocking socket operation could not be completed immediately

Here is my server side:

import socket
import select
import struct
import sys
import pickle

ProtoHeader = struct.Struct("!HI")
LoginRequest = 1 
MessageRequest = 2
ConversationRequest = 3
HEADER_LENGTH = 1024
conversation ={}
users = [
    {
        'username': 'user1',
        'user_id': 1
    },
    {
        'username': 'user2',
        'user_id': 2
    },
    {
        'username': 'user3',
        'user_id': 3
    },
    {
        'username': 'user4',
        'user_id': 4
    },
    {
        'username': 'user5',
        'user_id': 5
    }
]

def login(username):
    for user in users:
        if user['username'] == username:
            return user
        else: 
            return False
        

IP = "127.0.0.1"
PORT = 9669

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

server_socket.bind((IP, PORT))

server_socket.listen()

# List of sockets for select.select()
sockets_list = [server_socket]

# List of connected clients - socket as a key, user header and name as data
clients_socket = {}

sessions = {}
conversation = {
    (1,2) : '1.txt',

    (3,4) : '2.txt'
}


def getRecvSocket(user_id):
    try:
        return sessions[user_id]
    except:
        return None

# def sendErrorMes(socketid, mes):
#     package = [9]
#     length = len(mes)
#     if length > 1019:
#         length = 1019
#     package += struct.pack("I", length)
#     package += mes
#     package = pad(package)

print(f'Listening for connections on {IP}:{PORT}...')


def receive_bytes(conn, count):
    """ General purpose receiveer: 
        Receive exactly @count bytes from @conn"""
    buf = b''
    remaining = count 
    while remaining > 0:
        #Receive part of all of data 
        tbuf = conn.recv(remaining)
        tbuf_len = len(tbuf)
        if tbuf_len == 0: 
            #return 0 if buf is empty and allow the higher-level routine to determine
            #if the EOF is at a proper message boundary in which case, you silently close 
            #the connection. You would normally only raise an exception if you EOF in the 
            #middle of the message. 
            raise RuntimeError("end of file")

        buf += tbuf
        remaining -= tbuf_len

    return buf

def receive_proto_block(conn):
    """Receive the next protocol block from @conn. Return a tuple of request_type(interger)
       and payload (byte string) """
    
    proto_header = receive_bytes(conn, ProtoHeader.size)
    request_type, payload_length = ProtoHeader.unpack(proto_header)
    payload = receive_bytes(conn, payload_length)
    return request_type, payload

while True:

    read_sockets, _, exception_sockets = select.select(sockets_list, [], sockets_list)

    # Iterate over notified sockets
    for notified_socket in read_sockets:

        # If notified socket is a server socket - new connection, accept it
        if notified_socket == server_socket:

            client_socket, client_address = server_socket.accept()
            
            sockets_list.append(client_socket)
            
        else:
        # Receive message
            
            request_type, payload = receive_proto_block(notified_socket)
            print(request_type)
            print(payload.decode())
            if request_type == LoginRequest: 
                username = payload.decode()
                print(username)
                user = login(username)
                if user == False: 
                    notified_socket.send("no user found".encode())
                else: 
                    sessions[user["user_id"]] = notified_socket
                    print(sessions)
                    notified_socket.send(("Welcome to the server").encode())
            elif request_type == 2:
                recv_package = payload.decode()
                recv_id = recv_package[0]
                print(message)
                # if getRecvSocket(recv_id) == None:
                #     sendErrorMes(notified_socket, "User is offline")
                message = recv_package[1]
                for socket in sessions.values(): 
                    if socket == notified_socket: 
                        user = sessions[notified_socket]

        
                # fIterate over connected clients and broadcast message
                for client_socket in clients_socket:
                    # if clients[client_socket] == receive_user and client_socket != notified_socket:
                    # But don't sent it to sender
                    if client_socket != notified_socket and clients_socket[client_socket] == recv_id:

                        # Send user and message (both with their headers)
                        # We are reusing here message header sent by sender, and saved username header send by user when he connected
                        a = sessions[notified_socket]
                        b = recv_id 
                        with open(f"{conversation[a,b]}.txt", "w"):
                            f.write(user + message)


                        client_socket.send((user + "|" + message).encode())
                        
                if message is False:
                    # print('Closed connection from: {}'.format(user))

                    # Remove from list for socket.socket()
                    sockets_list.remove(notified_socket)

                    # Remove from our list of users
                    del clients_socket[notified_socket]

                    continue
        
            elif request_type == 3: 
                convo_id = payload.decode()
                if convo_id in conversation:
                    with open(conversation[convo_id], 'rb') as file_to_send:
                        for data in file_to_send:
                            notified_socket.sendall(data)
                    print('send successful')

                else: 
                    f = open(f"{len(conversation)+1}.txt", "w+")
        

and here is the console terminal for the server:

Listening for connections on 127.0.0.1:9669...
1
user1
user1
{1: <socket.socket fd=432, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9669), raddr=('127.0.0.1', 52919)>}
Traceback (most recent call last):
  File "c:/Users/Duong Dang/Desktop/bai 2.3/server.py", line 137, in <module>
    request_type, payload = receive_proto_block(notified_socket)
  File "c:/Users/Duong Dang/Desktop/bai 2.3/server.py", line 115, in receive_proto_block
    proto_header = receive_bytes(conn, ProtoHeader.size)
  File "c:/Users/Duong Dang/Desktop/bai 2.3/server.py", line 97, in receive_bytes
    tbuf = conn.recv(remaining)
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host

Thanks for the help.

0 Answers
Related