Working with Sockets

image with no caption

Now that we’ve used streams to communicate with the REPL and with files, let’s see how we can use them to communicate with another computer.

If you want to write a program that can communicate with another computer elsewhere on a standard network (almost all networks nowadays use the TCP/IP protocol), you’ll first need to create a socket. A socket is a mechanism for routing data over a computer network between programs running on different computers on that network.

Unfortunately, sockets didn’t make it into the ANSI Common Lisp standard, which means there’s no standard way of interacting with sockets at this time. However, every version of Common Lisp supports sockets, even if it doesn’t follow any standard. Since we’ve been using CLISP as our Lisp of choice in this book, we’ll consider only CLISP’s socket commands.

Note

cl-sockets (http://common-lisp.net/project/cl-sockets/) and usocket (http://common-lisp.net/project/usocket/) are two attempts at adding a standard socket library to Common Lisp.

Socket Addresses

Every socket within a network must have a socket address. This socket address has two components:

IP address

A number that uniquely identifies a computer on the network (typically shown as 4 bytes delimited by periods, such as 192.168.33.22).

Port number

Any programs that want to use the network must choose a unique port number that no other program on the same computer is already using.

The IP address and the port number combine to make up the socket address. Since the IP address is unique on a network and the port number is unique for a given computer, every socket address on a network is unique to a specific program running on a specific computer. Any messages running over the network (through chunks of data called TCP packets) will be labeled with a socket address to indicate their destination.

Once a computer receives a packet labeled with its IP address, the operating system will look at the port number in the socket address of the message to figure out which program should receive the message.

And how does the operating system know which program receives messages for a given port? It knows this because a program first must create a socket for that port in order to use it. In other words, a socket is simply a way for a computer program to tell the operating system, “Hey, if you get any messages on port 251, send them my way!”

Socket Connections

In order to actually send a message over a socket between two programs, we first need to follow some steps to initialize a socket connection. The first step in creating such a connection is to have one of the programs create a socket that starts in a listening state, waiting to see if other programs on the network want to start a communication. The computer with the socket in a listening state is called the server. Then the other program, called a client, creates a socket on its end and uses it to establish a connection with the server. If all goes well, these two programs can now transmit messages across the socket connection running between them.

But enough talk. Let’s try connecting two programs right now to see the magic happen for ourselves!

Sending a Message over a Socket

First, open two copies of CLISP in two different console windows on your computer. We’ll call one the client and one the server. (Or, if you have two computers on a network and know their IP addresses, you can create the two consoles on two separate machines, for the full network experience.)

Note

You must use CLISP to get the socket code shown in this chapter to run.

On the server, take control of a port by calling socket-server:

> (defparameter my-socket (socket-server 4321)) ;ON THE SERVER
MY-SOCKET

This command acquires port 4321 and binds a socket to it using the operating system. The socket is bound to the my-socket variable so that we can interact with it.

This command is somewhat dangerous, because the operating system is expecting us to give up the socket once we’re finished with it. If we don’t, no one will be able to use this socket anymore. In fact, if you make any mistakes during this socket exercise, you could mess up the socket at port 4321, and then you would need to switch to another port number until you restart your computer. (In the next chapter, you’ll learn how to use the exception handling system in Common Lisp to work around these ugly problems.)

Next, let’s make a stream from this socket (still on the server) that handles a connection from a single client:

> (defparameter my-stream (socket-accept my-socket)) ;ON THE SERVER

After running this command, the server will seem to lock up, and you won’t be returned to the REPL prompt. Don’t be alarmed—the socket-accept command is a blocking operation, which means the function won’t exit until a client has connected.

Now switch over to your client CLISP and use the socket-connect command to connect to that socket on the server:

> (defparameter my-stream (socket-connect 4321 "127.0.0.1")) ;ON THE CLIENT
MY-STREAM

The IP address 127.0.0.1 is a special address that always points to the computer from which it’s called. If you are using two different computers for this exercise, you should enter the actual IP address of your server.

After running this command, the server will unlock, and the value of the my-stream variable will be set. We now have a stream open in both copies of CLISP, and we can use it to communicate between them!

The stream CLISP has created here is called a bidirectional stream. This means it can act both as an input stream and an output stream, and we can use either set of commands on it to communicate in both directions. Let’s send a cordial greeting between the client and the server.

Enter the following on the client:

> (print "Yo Server!" my-stream)
"Yo Server!"

And enter the following on the server:

> (read my-stream)
"Yo Server!"

Then, still on the server, enter this:

> (print "What up, Client!" my-stream)
"What up, Client!"

Back on the client, run this command:

> (read my-stream)
"What up, Client!"

Here’s what your two CLISP windows should look like when you’re finished:

image with no caption

The message we sent across the socket was a Lisp string, but because of Lisp’s elegant stream-handling capabilities, we could send almost any standard Lisp data structure in the same way, without any extra effort!

Tidying Up After Ourselves

It’s crucial that we free up the resources we’ve created during this exercise. First, run the following command on both the client and the server to close the stream on both ends:

> (close my-stream)
T

Next, run socket-server-close on the server to free up the port and disconnect the socket from it. If you don’t, port 4321 will be unusable until you reboot.

> (socket-server-close my-socket)
NIL
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.37.12