LessonsExpert: BeyondLesson 57

Sockets

Connect two programs directly over the network: a server that accepts connections, a client that opens one, reading and writing with messages, errors, and a small protocol of your own.

  • Expert
  • Lesson 57 of 83
  • About 12 minutes
  • 5 examples

A line between two programs

A web request (The web and the internet) asks a question and gets one answer. A socket is a lasting two-way connection: both ends can write whenever they like, for as long as it stays open. Chat programs, multiplayer games and devices on a network all talk this way.

One program is the server: it listens on a port, a number from 1 to 65535 on its computer. The other is the client: it connects to the server's address and port. Once connected, the two are equal. In this lesson's examples one script plays both parts, on this computer (127.0.0.1), so they run anywhere.

The server: accept connections

accept connections on port starts listening. Each time a client connects, the message you name arrives with the new connection's id, which is the client's address and port, and is how you write to it, read from it and close it:

ScriptPut it in an object's script
on startServer
   accept connections on port 47123 with message "newClient"
   if the result is not empty then answer "Could not listen:" && the result
end startServer

on newClient pSocket
   -- pSocket is the connection, such as 127.0.0.1:52344
   write "Welcome!" & return to socket pSocket
end newClient

on stopServer
   close socket 47123
end stopServer

Closing the port stops the listening; connections already made stay open until they are closed too. Use a port above 1024 (lower ones need special permission), and one no other program on the computer uses.

The client: open socket

open socket to the address and port connects, and the message you name arrives once the connection is made. Nothing should be written before it does.

Writing and reading with messages

write ... to socket sends data. read from socket takes data that has arrived: until return for a line, or for a number of bytes. With with message, the read returns at once, and the message arrives with the data when it has all come, so the program never stops to wait for the other end. Here a client says hello and a server answers in capitals:

ExampleRun it in the playground
on newClient pSocket
   put "server: someone connected"
   read from socket pSocket until return with message "serverGotLine"
end newClient

on serverGotLine pSocket, pData
   put "server got:" && line 1 of pData
   write toUpper(pData) to socket pSocket
end serverGotLine

on connected pSocket
   put "client: connected"
   write "hello" & return to socket pSocket
   read from socket pSocket until return with message "clientGotLine"
end connected

on clientGotLine pSocket, pData
   put "client got:" && line 1 of pData
   close socket pSocket
   close socket 47123
end clientGotLine

accept connections on port 47123 with message "newClient"
open socket to "127.0.0.1:47123" with message "connected"
put "both ends are getting ready"
Output
both ends are getting ready
server: someone connected
client: connected
server got: hello
client got: HELLO

Follow the order of the lines: nothing happens during the script itself but setting things up. Everything after the first line happens in messages, as the data travels. That is the shape of every socket program: a handler for each thing that can arrive.

When things go wrong

Networks fail: the server is not there, the other end goes away, nothing arrives. The engine reports each with a message to the object that opened the socket: socketError with the socket and what went wrong, socketClosed when the other end closes, and socketTimeout when a read has waited longer than the socketTimeoutInterval:

ExampleRun it in the playground
on socketError pSocket, pError
   put "could not use" && pSocket & ":" && pError
end socketError

on socketClosed pSocket
   put pSocket && "was closed by the other end"
end socketClosed

open socket to "127.0.0.1:1" with message "connected"
put "trying a port where nobody listens"
Output
trying a port where nobody listens
could not use 127.0.0.1:1: Error 111 reading socket

the openSockets lists the connections and ports still open, a line each: check it to make sure a program closes what it opens.

A protocol of your own

The two ends must agree how a message ends, or the reader cannot tell one from the next: this agreement is a protocol. The simplest is a line each, as above, which suits text that has no line breaks in it. For anything else (text with returns, pictures, files) send the length first, and read exactly that many bytes:

ScriptPut it in an object's script
-- each message: its length as 4 bytes, then the bytes
command sendMessage pSocket, pText
   local tData
   put textEncode(pText, "UTF-8") into tData
   write binaryEncode("N", the number of bytes of tData) & tData to socket pSocket
end sendMessage

command listenFor pSocket
   read from socket pSocket for 4 bytes with message "gotLength"
end listenFor

on gotLength pSocket, pData
   local tLength
   get binaryDecode("N", pData, tLength)
   read from socket pSocket for tLength bytes with message "gotMessage"
end gotLength

on gotMessage pSocket, pData
   put textDecode(pData, "UTF-8") after field "Chat"
   listenFor pSocket
end gotMessage

Try it yourself

Write a calculator server: it reads lines such as add 2 3 or times 4 5, answers each with the result, and keeps reading. The client sends both questions, one after the other as each answer arrives, puts the answers, then closes.

Show a solution
ExampleRun it in the playground
local sQuestions

on newClient pSocket
   read from socket pSocket until return with message "serverGotLine"
end newClient

on serverGotLine pSocket, pData
   switch word 1 of pData
      case "add"
         write word 2 of pData + word 3 of pData & return to socket pSocket
         break
      case "times"
         write word 2 of pData * word 3 of pData & return to socket pSocket
         break
      default
         write "unknown" & return to socket pSocket
   end switch
   read from socket pSocket until return with message "serverGotLine"
end serverGotLine

on connected pSocket
   askNext pSocket
end connected

on askNext pSocket
   if sQuestions is empty then
      close socket pSocket
      close socket 47124
      exit askNext
   end if
   put line 1 of sQuestions into tQuestion
   delete line 1 of sQuestions
   put tQuestion && "is ..."
   write tQuestion & return to socket pSocket
   read from socket pSocket until return with message "clientGotAnswer"
end askNext

on clientGotAnswer pSocket, pData
   put "... " & line 1 of pData
   askNext pSocket
end clientGotAnswer

put "add 2 3" & return & "times 4 5" into sQuestions
accept connections on port 47124 with message "newClient"
open socket to "127.0.0.1:47124" with message "connected"
Output
add 2 3 is ...
... 5
times 4 5 is ...
... 20

Words in this lesson

Each one finds every lesson that uses it. The Dictionary has the whole story of each, with its syntax and more examples.