├── .gitignore ├── LICENSE ├── README.md ├── client.py └── server.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .AppleDouble 3 | ._* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Rishija 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | This is a simple multi-client chat server using `sockets` written in `python`. 3 | 4 | The server asks for username when user wants to join the chatroom and accepts the connection only if the username is unique. It then broadcasts the message from one client to all other clients connected. Also informs about the entry/exit of any client. 5 | 6 | ## Download 7 | Run the following command in your terminal to save the repository in your system 8 | > $ git clone https://github.com/Rishija/python_chatServer.git 9 | 10 | ## Run 11 | Once you are in the directory where `server.py` or `client.py` file exists, run by typing the following commands in your terminal. 12 | 13 | ### Server 14 | > $ python server.py 15 | 16 | ### Client 17 | > $ python client.py hostname 18 | 19 | #### Example 20 | For server and client running on the same system 21 | 22 | **Server** 23 | > $ python server.py 24 |
25 | 				SERVER WORKING 
26 | Client (127.0.0.1, 51638) connected  [ tesla ]
27 | Client (127.0.0.1, 51641) connected  [ albert ]
28 | Client (127.0.0.1, 51641) is offline  [ albert ]
29 | 
30 | 31 | 32 | 33 | **Client 1** 34 | > $ python client.py localhost 35 | 36 |
37 | CREATING NEW ID:
38 | Enter username: tesla
39 | Welcome to chat room. Enter 'tata' anytime to exit
40 | You: Hello
41 | albert joined the conversation 
42 | albert: world
43 | albert left the conversation
44 | You:
45 | 
46 | 47 | **Client 2** 48 | > $ python client.py localhost 49 |
50 | CREATING NEW ID:
51 | Enter username: albert
52 | Welcome to chat room. Enter 'tata' anytime to exit
53 | You: World
54 | You: tata
55 | DISCONNECTED!!
56 | 
-------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | import socket, select, string, sys 2 | 3 | #Helper function (formatting) 4 | def display() : 5 | you="\33[33m\33[1m"+" You: "+"\33[0m" 6 | sys.stdout.write(you) 7 | sys.stdout.flush() 8 | 9 | def main(): 10 | 11 | if len(sys.argv)<2: 12 | host = raw_input("Enter host ip address: ") 13 | else: 14 | host = sys.argv[1] 15 | 16 | port = 5001 17 | 18 | #asks for user name 19 | name=raw_input("\33[34m\33[1m CREATING NEW ID:\n Enter username: \33[0m") 20 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 21 | s.settimeout(2) 22 | 23 | # connecting host 24 | try : 25 | s.connect((host, port)) 26 | except : 27 | print "\33[31m\33[1m Can't connect to the server \33[0m" 28 | sys.exit() 29 | 30 | #if connected 31 | s.send(name) 32 | display() 33 | while 1: 34 | socket_list = [sys.stdin, s] 35 | 36 | # Get the list of sockets which are readable 37 | rList, wList, error_list = select.select(socket_list , [], []) 38 | 39 | for sock in rList: 40 | #incoming message from server 41 | if sock == s: 42 | data = sock.recv(4096) 43 | if not data : 44 | print '\33[31m\33[1m \rDISCONNECTED!!\n \33[0m' 45 | sys.exit() 46 | else : 47 | sys.stdout.write(data) 48 | display() 49 | 50 | #user entered a message 51 | else : 52 | msg=sys.stdin.readline() 53 | s.send(msg) 54 | display() 55 | 56 | if __name__ == "__main__": 57 | main() 58 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | import socket, select 2 | 3 | #Function to send message to all connected clients 4 | def send_to_all (sock, message): 5 | #Message not forwarded to server and sender itself 6 | for socket in connected_list: 7 | if socket != server_socket and socket != sock : 8 | try : 9 | socket.send(message) 10 | except : 11 | # if connection not available 12 | socket.close() 13 | connected_list.remove(socket) 14 | 15 | if __name__ == "__main__": 16 | name="" 17 | #dictionary to store address corresponding to username 18 | record={} 19 | # List to keep track of socket descriptors 20 | connected_list = [] 21 | buffer = 4096 22 | port = 5001 23 | 24 | server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 25 | 26 | server_socket.bind(("localhost", port)) 27 | server_socket.listen(10) #listen atmost 10 connection at one time 28 | 29 | # Add server socket to the list of readable connections 30 | connected_list.append(server_socket) 31 | 32 | print "\33[32m \t\t\t\tSERVER WORKING \33[0m" 33 | 34 | while 1: 35 | # Get the list sockets which are ready to be read through select 36 | rList,wList,error_sockets = select.select(connected_list,[],[]) 37 | 38 | for sock in rList: 39 | #New connection 40 | if sock == server_socket: 41 | # Handle the case in which there is a new connection recieved through server_socket 42 | sockfd, addr = server_socket.accept() 43 | name=sockfd.recv(buffer) 44 | connected_list.append(sockfd) 45 | record[addr]="" 46 | #print "record and conn list ",record,connected_list 47 | 48 | #if repeated username 49 | if name in record.values(): 50 | sockfd.send("\r\33[31m\33[1m Username already taken!\n\33[0m") 51 | del record[addr] 52 | connected_list.remove(sockfd) 53 | sockfd.close() 54 | continue 55 | else: 56 | #add name and address 57 | record[addr]=name 58 | print "Client (%s, %s) connected" % addr," [",record[addr],"]" 59 | sockfd.send("\33[32m\r\33[1m Welcome to chat room. Enter 'tata' anytime to exit\n\33[0m") 60 | send_to_all(sockfd, "\33[32m\33[1m\r "+name+" joined the conversation \n\33[0m") 61 | 62 | #Some incoming message from a client 63 | else: 64 | # Data from client 65 | try: 66 | data1 = sock.recv(buffer) 67 | #print "sock is: ",sock 68 | data=data1[:data1.index("\n")] 69 | #print "\ndata received: ",data 70 | 71 | #get addr of client sending the message 72 | i,p=sock.getpeername() 73 | if data == "tata": 74 | msg="\r\33[1m"+"\33[31m "+record[(i,p)]+" left the conversation \33[0m\n" 75 | send_to_all(sock,msg) 76 | print "Client (%s, %s) is offline" % (i,p)," [",record[(i,p)],"]" 77 | del record[(i,p)] 78 | connected_list.remove(sock) 79 | sock.close() 80 | continue 81 | 82 | else: 83 | msg="\r\33[1m"+"\33[35m "+record[(i,p)]+": "+"\33[0m"+data+"\n" 84 | send_to_all(sock,msg) 85 | 86 | #abrupt user exit 87 | except: 88 | (i,p)=sock.getpeername() 89 | send_to_all(sock, "\r\33[31m \33[1m"+record[(i,p)]+" left the conversation unexpectedly\33[0m\n") 90 | print "Client (%s, %s) is offline (error)" % (i,p)," [",record[(i,p)],"]\n" 91 | del record[(i,p)] 92 | connected_list.remove(sock) 93 | sock.close() 94 | continue 95 | 96 | server_socket.close() 97 | 98 | --------------------------------------------------------------------------------