Python Chat Server/Client Release/Tutorial - King of Hearts - Dec 15, 2013
Hey,
Today I'll be showing you how to make a chat server and client in python. But before I do, I'd like to thank āšÛŖÛ1x1x1x1 for his tutorial on networking in python which caught my eye and showed me how awesome python really is. This is my first program in python, so feel free to leave constructive feedback. Note: Even though this was my first program in python, it is by no means my first program. I'm going to assume, you have at least some knowledge of programming.
You can view the full source code here
Now on with the tutorial. We will first be writing the server.
First we will import three things. Go ahead and
PHP Code
<?php import socket import sys import thread
Next we will make the socket. The socket is the connection handler. It receives the data the client will send us. The socket is going to be of type TCP (Google this if you need to). We will then bind it to localhost and any random port (should be above 1050). Next we will specify how many clients at most to handle simultaneously.
PHP Code
<?php mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host = "localhost" port = 9992 numberOfClients = 50 mysocket.bind((host, port)) mysocket.listen(numberOfClients)
And then some print statements and initializing a dictionary to store connected clients.
PHP Code
<?php print '--' print 'Listening at ' + host + ':' + str(port) print '--' connected = {}
After this, I made two functions. The first is called acceptNewConnections and as the name suggests it accepts new connections. I've added comments to show you what the code is doing.
PHP Code
<?php def acceptNewConnections(): while 1:#always listen for new connections try: conn, addr = mysocket.accept()#accept a new connection name = conn.recv(1024)#the first connection will always be the name (done client sided) if name:#checks to make sure the client did send something if name in connected:#check if name is taken by someone already conn.send('SERVER:NAME CHOSEN')#If it is taken tell that to the client conn.close()#close the connection else:#if name isn't taken print name + ' Connected' connected[name] = (conn, addr)#add the connection to the dictionary with key being name print str(len(connected)) + " Users connected" thread.start_new_thread(listen, (connected[name][0], connected[name][1], name))#starts the thread which will listen to connections from that socket. #if you don't know what threads are, they allow for your program to do more than 1 thing at once. connectedNames = '' for nameF,connF in connected.iteritems():#loop through the connected clients to tell new user joined if not nameF is name:#make sure the new user isn't being told he joined connF[0].send(name + " Joined the chat") connectedNames += nameF + ', '# add the name of connected user to connectednames string conn.send('SERVER:There are currently ' + str(len(connected)) + ' connected users: ' + connectedNames + name + '\nType ::help for help') #send new user welcome message except Exception, e: print 'Erroraa ' + str(e) pass
The second function I made was the listen function. This one also has comments to help you out:
PHP Code
<?php def listen(conn,addr,name):#takes in the connection, the ip adress of connection and name as parameters while 1:#always listens to specified client try: data = conn.recv(1024)#receives data with 1024 buffersize (if you don't know what it is leave it as is) print str(data) for nameF,connF in connected.iteritems():#loop through connected clients if not nameF is name:#make sure the client isn't the same one who send the data connF[0].send(data)#send the data to the other user except Exception, e: if '10054' in str(e):#this error means the connection is closed print name + ' Left' del connected[name]#delete the connection from the connected dictionary for nameF,connF in connected.iteritems():#loop through connected clients connF[0].send(name + " Left")#tell them that the user left #notice there is no if here because I've already deleted the current connection from the connected dictionary thread.exit()#stop the thread else: print str(e) break
Lastly, I started the acceptConnections Thread and set a while loop to keep the main thread running as well:
PHP Code
<?php try: thread.start_new_thread(acceptNewConnections, ()) except Exception, e: print str(e) print "Error: unable to start thread"
while 1: pass
Now the Client:
Again start with the imports:
PHP Code
<?php import socket import thread import sys
Now here I defined the two main functions first. The first function is the send function which listens for user input and sends it to the client.
PHP Code
<?php def send(): while 1:#always listen for input str = raw_input("")#gets input from user try: if str == '::exit':#checks if str is a command to exit mysocket.close()#if it is it's going to close the socket sys.exit(0)#and exit the program elif str == '::help':#else if the str is another command for help print '\nHelp: \n\tTo exit tpe ::exit'#print help (you can add more stuff/commands if you'd like) else: mysocket.send(name + ": " + str)#send the server the name of the client + message except Exception, e: if '10054' in e:#means the server went offline raw_input('Server disconnected. Press enter to exit') sys.exit(0)
The next function is the listen function. This method listens for the data from the server and prints it.
PHP Code
<?php def listen(): while 1:#always listen try: data = mysocket.recv(1024)#get the data print ">>" + data#print it with >> to show it's from the server/another user except Exception, e: if '10054' in str(e):#if server went offline: raw_input('Server disconnected. Press enter to exit') sys.exit(0)
Above, I defined the functions. However, I haven't called them or done anything else with the program yet. Now I'll make a socket and try to connect to the server:
PHP Code
<?php mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#make a new TCP socket in the IPv4 protocol mysocket.ioctl(socket.SIO_KEEPALIVE_VALS, (1, 30000, 30000))#make it so that it doesn't die out anytime soon. #TCP sockets close after a certain amount of connections, here I set the amount to be 30000 host = "localhost"#server to connect to port = 9992#port try: mysocket.connect((host, port))#try to connect to the server except Exception, e: print 'something\'s wrong. Exception type is %s' % (`e`) raw_input('Can\'t connect. Press enter to close')#can't connect so close sys.exit(0)
Now that I'm connected, I have to set my name and make sure it's available:
PHP Code
<?php name = raw_input('Please enter your name: ')#ask user for the name mysocket.send(name)#send it to the server data = mysocket.recv(1024)#receive the response while 1: if 'NAME CHOSEN' in data:#see if response tells us if the name is chosen mysocket.close();#if it is first close the current socket mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#remake the socket mysocket.ioctl(socket.SIO_KEEPALIVE_VALS, (1, 30000, 30000))#reset the minimum connection value try: mysocket.connect((host, port))#connect to the server again except Exception, e: print 'something\'s wrong. Exception type is %s' % (`e`) raw_input('Can\'t connect. Press enter to close') sys.exit(0) name = raw_input('That name is chosen. Enter a different name ');#ask for the name again mysocket.send(name);#send the name again data = mysocket.recv(1024)#reset data to equal the response and go back to the top of the loop else:#if name is available print data#print the welcome message break#get out the infinite loop because the name is available and we are connected
Now that we are connected all we have to do is start the listen and send threads:
PHP Code
<?php try: thread.start_new_thread(send, ())#start the send thread thread.start_new_thread(listen, ())#start the listening thread except: print "Error: unable to start thread"
while 1:#keep main thread running pass
Feel free to pm me for help or post here. As a challenge, you should try to add private messaging.
It only takes 2 seconds to say thanks.
* - Invicta - Dec 15, 2013
It actually took me longer than two seconds to say thanks, but this is a nice contribution.
* - _UserName_ - Dec 16, 2013
Good tutorial. I only wonder why you changed the condition in the while loop every time you used one.
* - Kansas - Dec 16, 2013
I'm still fairly new to python, and this is definitely a project I'm going to try to tackle when I'm a little more experienced. Bookmarked.
Oh and seeing this, infinite loops are okay in python? Because coming from php those are almost always a no-no.
* - dberube - Dec 16, 2013
Nice tutorial. Everything explained and well divided đ .
Just a few remarks:
1: The code is pretty long and reading it inside php code tag gave me a headache. Maybe posting the source on pastbin and add the link at the end of your tutorial?
2: Try to separate comments from the code. (Maybe hackforums destroyed your indent, but hey! that's another reason to post it on pastebin!)
ie:
Code mycode [this is a place holder so hackforum wont destroy my indent] . . mycomment
while i_have_a_cookie: [this is a place holder so hackforum wont destroy my indent] mycomment
etc...
This đ:
Code #Nope
while 1==1:
#Good!
while 1:
* - King of Hearts - Dec 16, 2013
(Dec 16, 2013 09:26 AM)dberube Wrote: Nice tutorial. Everything explained and well divided đ .
Just a few remarks:
1: The code is pretty long and reading it inside php code tag gave me a headache. Maybe posting the source on pastbin and add the link at the end of your tutorial?
2: Try to separate comments from the code. (Maybe hackforums destroyed your indent, but hey! that's another reason to post it on pastebin!)
ie:
Code mycode [this is a place holder so hackforum wont destroy my indent] . . mycomment
while i_have_a_cookie: [this is a place holder so hackforum wont destroy my indent] mycomment
etc...
This đ:
Code #Nope
while 1==1:
#Good!
while 1:
Thanks for your feedback. I'll change the code now. As for tutorials, yes I had to add it on the second line because the page became really long in the horizontal direction if I kept some comments on the same line. I really don't want to use pastbin, I'd rather keep it on HF.
* - Melkor333 - Dec 16, 2013
woww thx alot for that tutorial! i just told me to try doing something like this the next days and then see your tutorial :D
But i have to agree with dberube. It was really awesome if you could upload the whole Server and Client program!
* - Ne0nWorm - Dec 17, 2013
Code mysocket.ioctl(socket.SIO_KEEPALIVE_VALS, (1, 30000, 30000))#make it so that it doesn't die out anytime soon.
Returns: AttributeError: '_socketobject' object has no attribute 'ioctl'
There are other ways to make a socket not time out , just letting you know, and Im running python 2.7.3.
* - blem - Dec 17, 2013
If you want another challenge you can add AES encryption to it using https://www.dlitz.net/software/pycrypto/
Nice tutorial
(Dec 17, 2013 04:12 AM)Ne0nWorm Wrote: Code mysocket.ioctl(socket.SIO_KEEPALIVE_VALS, (1, 30000, 30000))#make it so that it doesn't die out anytime soon.
Returns: AttributeError: '_socketobject' object has no attribute 'ioctl'
There are other ways to make a socket not time out , just letting you know, and Im running python 2.7.3.
It's windows only.
* - Ne0nWorm - Dec 17, 2013
(Dec 17, 2013 04:30 AM)Astound Wrote: If you want another challenge you can add AES encryption to it using https://www.dlitz.net/software/pycrypto/
Nice tutorial
It's windows only.
![[Image: tAgt9WA.png]](https://i.imgur.com/tAgt9WA.png)
Thats why i stated there are other ways to keep a socket alive :)
* - King of Hearts - Mar 18, 2014
It's been a while since this has been on the first page. A lot of newcomers might find this useful.
* - #BORNE - Mar 19, 2014
Like the guy said on the first page I am also quite new to Python but I hope to tackle project like this in the future.
* - King of Hearts - Mar 28, 2014
(Mar 19, 2014 09:35 AM)Masked Wrote: Like the guy said on the first page I am also quite new to Python but I hope to tackle project like this in the future.
Honestly, python is an easy language. I wrote this in my first day of learning python. I hope you find this useful.
* - glob - Mar 28, 2014
HF fucked your indentation and again as dberube said, it gives people a headache to read that much code in php tags.
Granted using the addition operator for string concatenation is a bit childish when you could use .format or strf syntax don't you think?
* - creepycreeper - Mar 29, 2014
actually good one - only wrong stuff I noticed (apart Win only stuff which was already mentioned) is "Next we will specify how many clients at most to handle simultaneously." - that specifies, how many pending connection requests are possible (and according to documentation is usually maximum possible 5), not how many clients there can be altogether
( I'm slightly disappointed, though - after half a year back to HF, I open randomly tutorial on first page of python section, looking forward to see some really bad stuff again, then even reading "This is my first program in python" and telling myself "yeah, nothing changed, again some noob writing tutorial, even though he knows shit" and then actually above average client-server tutorial...)
|