Welcome to HBH! If you have tried to register and didn't get a verification email, please using the following link to resend the verification email.

Threading and Sockets in Python


Threading and Sockets in Python

By richohealey avatarrichohealey | 9503 Reads |
0     0

First off, all code within this article is my own work, and while I encourage you to use this program and it's parts, if you use big chunks, please credit me. Also, because HBH has no TAB support (ugh) i've just left this unformatted. for functional code go to the URL at the bottom.

I'm assuming that you have a moderate background in python.

Now, sockets and threading, two things that are a genuine pain in the arse, until you get to know them. A brief overview:

SOCKETS:

These are the primary way that data is transmitted over an IP connection. You create an open socket on one machine, which opens a port (always use at least 5 digits to avoid overlap), and can then be connected to by another machine. From here data sent by the machine that connects is buffered and recieved by the machine withg the open socket. You can have 1 connection per port, remember that the port of the connecting socket must match the recieving one.

THREADING:

Threading is a way of having a computer do more than one thing at once. It allows you to set a task running, and then have the computer go off and do something else while still performing the first task. Used in games, to facilitate AI, and look for input from player among many other things. In a low level language like C++ it can often be just as efficient to write a well constructed sequence of checks, rather than threads, but in an interpreted language like python the change is speed is generally quite dramatic.

Now onto the Python side of things.

First sockets. #begin sample socket client code (this is the bit to send messages)

import socket #needed to use sockets from socket import * # we need everything class SockClient(object): #we're making a class for our client def init(self, host, port): #the init() method is run whenever a class is called self.host = host #making the inputs native to the class, so we can use them repeatedly. self.port = port #for this example it's not actually necessary, but a good habit to get into self.addr = (self.host, self.port)

    sock = None
    for res in getaddrinfo(self.host, self.port, AF_UNSPEC, SOCK_STREAM): #this outputs a list with the details about the coket type we\'ll be using. just trust me here, to go into more detail is way beyond the scope of this article
        af, socktype, proto, canonname, sa = res        #using the for loop is the easiest way to extract the data from the list
        try:
            sock = socket(af, socktype, proto)        #creating a socket, passing on the data we just collected
        except error, msg:                           #error handling, means we get a nice(ish) message if something goes wrong
            sock = None
            continue
        try:
            sock.connect(sa)            #try to connect to our server, sa is a tuple with our reformatted host and port data
        except error, msg:               #more error handling
            sock.close()
            sock = None
            continue
        break
    if sock is None:
        raise RuntimeError, \"could not connect socket\"

    self.sock = sock                 #finally makes the socket callable
def remotemsg(self,msg):             # this function sends a message, and takes one input
    self.sock.send(msg)
    del msg
def close(self):                     # a funtion to cleanly kill the socket
    self.sock.shutdown(SHUT_RDWR)
    self.sock.close()

if you followed this right, and run it in a terminal, you now have a chat client. but wait nothing to connect to….

so

#begin sample socket server code

import socket from socket import *

class SockServer(object): def init(self, port): self.port = port #again, we define a class, and make all the inputs local to it self.host = '' self.addr = (self.host, self.port) self.buf = 1024 # this variable will tell it how many characters to go looking for when it finds a message

    sock = None
    for res in getaddrinfo(self.host, self.port, AF_UNSPEC, SOCK_STREAM, 0, AI_PASSIVE): #again, we get the addressinfo, but the 0 and AI_passive tags tell it that this will be recieving, not sending
        af, socktype, proto, canonname, sa = res
        try:
            sock = socket(af, socktype, proto)
        except error, msg:
            sock = None                                #again, error handling
            continue
        try:
            sock.bind(sa)     # here it is binding it to a local port, so that it can listen to the outside world
            sock.listen(1)    # listening for connection
        except error, msg:
            sock.close()
            sock = None
            continue
        break
    if sock is None:
        raise RuntimeError, \"could not open socket\"

    self.sock,addr = sock.accept()  #accept the first connection it finds
    self.mainloop()
def mainloop(self):
    while 1:                     #endless while loop
        data = self.sock.recv(self.buf)      #see if the other user has sent us anything
        if not data:                         # if data is empty, they\'ve left, so we should too
                print \"Remote user has terminated the session.\"
                break
        else:
                print \'remote_user:\',data    #otherwise, show us what they wrote!
                print \"message?:\"
                del data
    self.close()
def close(self):
    self.sock.shutdown(SHUT_RDWR)           #again, a clean kill
    self.sock.close()

so now if we run these on diffent machines, and open the server first, then the client, we can send messages one way.

but we want to chat, which means we need threading.

threading incorporates the threading module, and jsut means that the thread will do what it's doing in the background. to illustrate this i'll write a thread that adds a number to a variable 'a' whenever 'a' has a value #begin threading example import threading #so we can use threads

class OurThread(threading.Thread): #this isn't an object, it's a thread so we tell it def init(self, factor): # we want to take input, so we re-write the threading init() function self.factor = factor #make it local threading.Thread.init(self) #we still need the threading init() function def run(self): global a #tell it to look for any instance of a variable a while 1: if a != None: a +=self.factor # if a exists add 2, print it, and then make it None again print a a = None

now if we execute OurThread(2).start() and then type a=2 it will print 4 this is very handy, and a very simple example, but will tell you enough to play around.

now a full blown example:

import threading import socket #import the stuff we need from socket import *

class SockClient(object): def init(self, host, port): self.host = host self.port = port self.addr = (self.host, self.port)

    sock = None
    for res in getaddrinfo(self.host, self.port, AF_UNSPEC, SOCK_STREAM):
        af, socktype, proto, canonname, sa = res
        try:
            sock = socket(af, socktype, proto)
        except error, msg:
            sock = None
            continue
        try:
            sock.connect(sa)              #the same as our old client
        except error, msg:
            sock.close()
            sock = None
            continue
        break
    if sock is None:
        raise RuntimeError, \"could not connect socket\"

    self.sock = sock
def remotemsg(self,msg):
    self.sock.send(msg)
    del msg
def close(self):
    self.sock.shutdown(SHUT_RDWR)
    self.sock.close()

class SockServer(threading.Thread): def init(self, port): self.port = port threading.Thread.init(self) def run(self): self.host = '' self.addr = (self.host, self.port) self.buf = 1024

    sock = None
    for res in getaddrinfo(self.host, self.port, AF_UNSPEC, SOCK_STREAM, 0, AI_PASSIVE):
        af, socktype, proto, canonname, sa = res
        try:
            sock = socket(af, socktype, proto)
        except error, msg:
            sock = None
            continue
        try:
            sock.bind(sa)                       #the same as our old server, but implemented as a thread
            sock.listen(1)
        except error, msg:
            sock.close()
            sock = None
            continue
        break
    if sock is None:
        raise RuntimeError, \"could not open socket\"

    self.sock,addr = sock.accept()
    self.mainloop()
def mainloop(self):
    while 1:
        data = self.sock.recv(self.buf)
        if not data:
                print \"Remote user has terminated the session.\"
                break
        else:
                print \'remote_user:\',data
                print \"message?:\"
                del data
    self.close()
def close(self):
    self.sock.shutdown(SHUT_RDWR)
    self.sock.close()

send_ip = raw_input('What is other ip?') send_port = input('What is other port?') open_port = input('What is your open port?') #finally a system for managing input and output SockServer(open_port).start() fake = raw_input('Press return when you both have this message.') insock = SockClient(send_ip, send_port) while 1: print 'Message?:' msg = raw_input() if msg: insock.remotemsg(msg)

try this code, it works well. I've uploaded this article at www.freewebs.com/richohealey/python/threadart.txt and just the code segments at www.freewebs.com/richohealey/python/threadcode.txt you just have to chop it up into the indivdual programs and save as something.py the uncommented funtional chat code is at www.freewebs.com/richohealey/python/chat.py

thanks for reading.

-RichoHealey

Comments
ghost's avatar
ghost 18 years ago

:D you sure know you're Python

richohealey's avatar
richohealey 18 years ago

yeah. good language. i realise that this is pretty advanced, but i figure sooner or later someone wil benifit.

ghost's avatar
ghost 18 years ago

Wow… I'm actually working on something just like this at the moment (although much more simple)…

Thanks alot, this will be a really helpful guideline.

ghost's avatar
ghost 17 years ago

truly the "Python God" … well done, dude.