Advertisement
den4ik2003

Untitled

Nov 25th, 2024
70
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.90 KB | None | 0 0
  1. import socket
  2. import select
  3.  
  4. HEADER_LENGTH = 10
  5.  
  6. IP = "127.0.0.1"
  7. PORT = 1234
  8.  
  9. # Create a socket
  10. # socket.AF_INET - address family, IPv4, some otehr possible are AF_INET6, AF_BLUETOOTH, AF_UNIX
  11. # socket.SOCK_STREAM - TCP, conection-based, socket.SOCK_DGRAM - UDP, connectionless, datagrams, socket.SOCK_RAW - raw IP packets
  12. server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  13.  
  14. # SO_ - socket option
  15. # SOL_ - socket option level
  16. # Sets REUSEADDR (as a socket option) to 1 on socket
  17. server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  18.  
  19. # Bind, so server informs operating system that it's going to use given IP and port
  20. # For a server using 0.0.0.0 means to listen on all available interfaces, useful to connect locally to 127.0.0.1 and remotely to LAN interface IP
  21. server_socket.bind((IP, PORT))
  22.  
  23. # This makes server listen to new connections
  24. server_socket.listen()
  25.  
  26. # List of sockets for select.select()
  27. sockets_list = [server_socket]
  28.  
  29. # List of connected clients - socket as a key, user header and name as data
  30. clients = {}
  31.  
  32. print(f'Listening for connections on {IP}:{PORT}...')
  33.  
  34. # Handles message receiving
  35. def receive_message(client_socket):
  36.  
  37.     try:
  38.  
  39.         # Receive our "header" containing message length, it's size is defined and constant
  40.         message_header = client_socket.recv(HEADER_LENGTH)
  41.  
  42.         # If we received no data, client gracefully closed a connection, for example using socket.close() or socket.shutdown(socket.SHUT_RDWR)
  43.         if not len(message_header):
  44.             return False
  45.  
  46.         # Convert header to int value
  47.         message_length = int(message_header.decode('utf-8').strip())
  48.  
  49.         # Return an object of message header and message data
  50.         return {'header': message_header, 'data': client_socket.recv(message_length)}
  51.  
  52.     except:
  53.  
  54.         # If we are here, client closed connection violently, for example by pressing ctrl+c on his script
  55.         # or just lost his connection
  56.         # socket.close() also invokes socket.shutdown(socket.SHUT_RDWR) what sends information about closing the socket (shutdown read/write)
  57.         # and that's also a cause when we receive an empty message
  58.         return False
  59.  
  60. while True:
  61.  
  62.     # Calls Unix select() system call or Windows select() WinSock call with three parameters:
  63.     #   - rlist - sockets to be monitored for incoming data
  64.     #   - wlist - sockets for data to be send to (checks if for example buffers are not full and socket is ready to send some data)
  65.     #   - xlist - sockets to be monitored for exceptions (we want to monitor all sockets for errors, so we can use rlist)
  66.     # Returns lists:
  67.     #   - reading - sockets we received some data on (that way we don't have to check sockets manually)
  68.     #   - writing - sockets ready for data to be send thru them
  69.     #   - errors  - sockets with some exceptions
  70.     # This is a blocking call, code execution will "wait" here and "get" notified in case any action should be taken
  71.     read_sockets, _, exception_sockets = select.select(sockets_list, [], sockets_list)
  72.  
  73.  
  74.     # Iterate over notified sockets
  75.     for notified_socket in read_sockets:
  76.  
  77.         # If notified socket is a server socket - new connection, accept it
  78.         if notified_socket == server_socket:
  79.  
  80.             # Accept new connection
  81.             # That gives us new socket - client socket, connected to this given client only, it's unique for that client
  82.             # The other returned object is ip/port set
  83.             client_socket, client_address = server_socket.accept()
  84.  
  85.             # Client should send his name right away, receive it
  86.             user = receive_message(client_socket)
  87.  
  88.             # If False - client disconnected before he sent his name
  89.             if user is False:
  90.                 continue
  91.  
  92.             # Add accepted socket to select.select() list
  93.             sockets_list.append(client_socket)
  94.  
  95.             # Also save username and username header
  96.             clients[client_socket] = user
  97.  
  98.             print('Accepted new connection from {}:{}, username: {}'.format(*client_address, user['data'].decode('utf-8')))
  99.  
  100.         # Else existing socket is sending a message
  101.         else:
  102.  
  103.             # Receive message
  104.             message = receive_message(notified_socket)
  105.  
  106.             # If False, client disconnected, cleanup
  107.             if message is False:
  108.                 print('Closed connection from: {}'.format(clients[notified_socket]['data'].decode('utf-8')))
  109.  
  110.                 # Remove from list for socket.socket()
  111.                 sockets_list.remove(notified_socket)
  112.  
  113.                 # Remove from our list of users
  114.                 del clients[notified_socket]
  115.  
  116.                 continue
  117.  
  118.             # Get user by notified socket, so we will know who sent the message
  119.             user = clients[notified_socket]
  120.  
  121.             try:
  122.                 print(f'Received message from {user["data"].decode("utf-8")}: {message["data"].decode("utf-8")}')
  123.             except UnicodeDecodeError:
  124.                 print(f'Received message from {user["data"].decode("utf-8")}: can not decode')
  125.  
  126.             # Iterate over connected clients and broadcast message
  127.             for client_socket in clients:
  128.  
  129.                 # But don't sent it to sender
  130.                 if client_socket != notified_socket:
  131.  
  132.                     # Send user and message (both with their headers)
  133.                     # We are reusing here message header sent by sender, and saved username header send by user when he connected
  134.                     client_socket.send(user['header'] + user['data'] + message['header'] + message['data'])
  135.  
  136.     # It's not really necessary to have this, but will handle some socket exceptions just in case
  137.     for notified_socket in exception_sockets:
  138.  
  139.         # Remove from list for socket.socket()
  140.         sockets_list.remove(notified_socket)
  141.  
  142.         # Remove from our list of users
  143.         del clients[notified_socket]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement