• We're going to talk about sockets and how you use them in code, how you write code to talk on networks using sockets.


  • The socket interface - write code that communicates on a network. So if you're not writing code that communicates on a network, you can not worry about sockets.


  • The socket interface is the most common way to talk on the internet or to programatically communicate on the internet through a piece of code.


  • You can create sockets in Python and that's what we're using because that's how we're programming on the Raspberry Pi.


  • Socket communication is based on a client/server programming model. So, it assumes client/server interface.


  • So, we're going to make a generic client in Python. And so this client is going to talk, it's going to connect to some server and it's going to request something and get a result back.


  • So, here's what the outline of the structure of such a piece of code, a code for a client would look like.


    1. First, create a socket.


    2. Next you connect it to the server's socket.


    3. Then you send your data.


    4. Wait for response


    5. Then once you receive it, you close the socket.


  • So here's some Python code that would do that.


  •  
    
    import socket
    
    mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    
    
  • First you import the socket. The socket package has all these socket library functions in it


  • Next, socket.socket() - method creates a socket.


  • socket method takes two arguments.


  • The first argument is socket.AF_INET. So AF_INET declares the Address Family to be Internet.


  • That is always what we're going to be using, because we're talking on the internet.


  • Now there are other address families for other types of networks. But by far the most common's going to be Internet.


  • Say we wanted to do infrared communication, there's an infrared address family and so on.


  • Also, the next argument SOCK_STREAM, SOCK_STREAM indicates that we're using TCP, connection-based communication as compared to UDP which is conectionless.


  • We're using TCP and remember TCP gives us more overhead but TCP gives us flow control, it gives us reliable communication.


  • So we know that if we send a message it will be received. It won't get dropped.


     
    
    host = socket.gethostbyname("www.google.com")
    mysock.connect((host,80))
    
    
    
  • The basic things you do with sockets once they're created - send data on the socket, and receive data on socket.


  • mysock.connect() will connect the socket to a remote machine or to an application on a remote machine.


  • connect() takes one argument which is a tuple of two things. That tuple contains host, number.


  • Now, in this case, host is the IP address of the host that you want to connect to.


  • The second part of that tuple is a number which is the port number that you want to connect to.


  • So, the fact that I'm now connecting to port 80 tells me that I'm connecting to a web server, because port 80 is associated with web servers.


  • The first part of that tuple which is the argument is the host. That's supposed to be the IP address or representation of the IP address. But, remember that usually humans don't remember IP addresses.


  • So, that's what the first line of code is doing. The host is an IP address, but you may only have the domain. So, you call this function gethostbyname() to perform a DNS lookup.


  • DNS is Domain Name Service lookup. It will lookup the IP address for a particular domain name, and it'll return that.


  • socket.gethostbyname() - pass it the string which is the domain name, it will return this host which is the IP address.


  • It will look locally, maybe you have it in your local cache, otherwise it'll go your name server and ask it, where can I find it, the name saver might ask another name server and so on.


  • gethostbyname() - it comes back with an IP address. Then, once you have the IP address as host, then you can call mysock.connect(), and pass it one argument which is a tuple of the host, IP address, and the port that you want to connect to depending on the application you want to speak to.


  • connect() - it creates a connection or opens the connection between the two. The port 80 is for web traffic, but it can be whatever port you want to connect to. So, now once you've got that connected, you want to send some data on a socket.


  •  
    
    message = "GET / HTTP/1.1\r\n\r\n"
    
    mysock.sendall(message)
    
    data = mysock.recv(1000)
    mysock.close()
    
    
  • The sendall() method - it sends data.


  • The first line message is actually an HTTP GET request.


  • The most simple GET request, this is the most simple you can get.


  • There are basic three things in the message.


  • If you look at that message, it says GET, the first word is GET. It's the type of request.


  • There are a few types of HTTP requests.


  • A GET request is the most common very basic.


  • It just says, give me data off of this webpage.


  • Basically all it says, give me the data off this webpage. So, it's asking you to return it with the data that it finds there. Now, then, the next thing on that line is a slash.


  • The slash tells you which file you want to get off of the server.


  • Now, it's just slash. So, that just means the local directory. It doesn't tell me the name of a file there.


  • Whenever you don't give it a name of file like that, it assumes the name of the file is called index.html. That's the generic webpage name.


  • Then, the third thing on the line is the version of HTTP. So, it's version 1.1.


  • Then, at the end of the line, there is \r\ n\r\n. That character and line feed, character and line feed that double like that.


  • In HTTP, it marks the end of a request. So, that's how it knows that that's the end of the request when you put the \r\n\r\n.


  • So, by sending that, I'm requesting Google's webpage. So, then I might want to receive data on the socket.


  • Now, typically after sending a request like that, you'll want to receive whatever the results are.


  • You're expecting Google to send you back a webpage or some data, and so you want to receive data on that.


  • So, this time we call mysock.recv. Now, it takes an argument. In this case 1000 we're passing as an argument.


  • That is the maximum number of bytes that you can receive, that you're expecting to receive on this socket. That's the maximum that you receive.


  • mysock.recv will return some number of bytes that are received on the socket, and they will go to data.


  • So, data will be assigned to whatever is received on the socket


  • This is a blocking wait so when you're executing this code, the code execution will stop at that line until something is received.


  • It will block on that line. It won't continue past that line until something is received.


  • The argument is just a maximum number of bytes to receive, which is important in certain languages where you have these fixed length buffers.


  • You don't want to overflow the buffer.


  • Now, then closing a socket is what you do after you're done with the communication.


  • mysock.close() - It will close the socket, which is good because then you can reuse a socket later, or if you want to connect to something else. So, free up the port.


  • Exception handling is useful, because sockets can go wrong and we want to able to catch that.


  • Any Python program can have errors during execution, and part of writing really robust code is handling those errors.


  • So anticipating classes of things that might go wrong, and then handling them in some way.


  • So errors can happen when you're working with sockets.


  • So you try to open a socket on another end, and you can't open it. Maybe the server is not there, or something like that


  • Data can't be sent for some reason, data might be blocked. This type of thing happens in networks


  • So there are lots of errors that can happen when you're using sockets. And you want to be able to handle these errors.


  • You don't want your machine to just, your code to just crash every time. Something like that happens, you like it to be able to print a response, tell you what's going on at least.


  • So when errors happen during execution, they're called exceptions.


  • And this isn't just with sockets, this is just in general, in Python or other languages you have what are called exceptions.


  • Exceptions that are raised, or thrown, however you want to call it.


  • There are many types of exceptions that can be raised for different functions


  • So the exception type, tells you what kind of thing went wrong.


  • So for instance, you can have an exception, a ZeroDivisionError and if that exception happens if somebody tried to divide by zero.


  • You can have a TypeError, which means that you tried to perform an operation on the wrong type, like you tried to perform an add, or a floating point add on an integer


  • And sockets have their own set of errors and exceptions that we're going to want to handle for at least a sub set of them.


  • So the way you do this is you use this try and accept statements.


  •  
    
    try:
        z = x / y
    except ZeroDivisionError: 
        print("Error division by zero")
        
    
    
  • So there are try and accept statements that are built into Python, which allow you to handle errors and exceptions as they come up. So what you do is, you place the code with the possible error, that you think will raise the exception, you place that inside a try clause. Example of this is given above.


  • Then, after that try clause you have what's called an except clause. And the except clause contains code that you want to execute when the try clause throws the exception, or raises the exception.


  • socket.error that is an exception that is just generic. Any socket error is going to fall into this class of socket.error. That's just the most generic type of socket exception that can be raised.


  • gaierror - The gai stands for getaddressinfo.error


  • And remember, getaddressinfo that does a DNS lookup, so you tell it www. Google.com, you pass that as an argument to getaddressinfo, it'll tell you the IP address.


  • If there's an error in that process, it doesn't find that host address, then it will raise a gaierror.


  • So those are the two exception classes that we want to be able to handle. So we're going to change our code.


  •  
    import sys
    import socket
    
    try:
        mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    except socket.error:
        print("Failed to created socket")
        sys.exit()
    
    try:
        host = socket.gethostbyname("www.google.com")
    except socket.gaierror:
        print("Failed to get host")
        sys.exit()
    
    mysock.connect((host,80))
    message = "GET / HTTP/1.1\r\n\r\n"
    
    try:
        mysock.sendall(message)
    except socket.error:
        print("Failed to send")
        sys.exit()
    
    data = mysock.recv(1000)
    print("data")
    mysock.close()