Protosockets only work with TCP connections.
The protosocket library uses Protothreads protothreads to provide sequential control flow. This makes the protosockets lightweight in terms of memory, but also means that protosockets inherits the functional limitations of protothreads. Each protosocket lives only within a single function block. Automatic variables (stack variables) are not necessarily retained across a protosocket library function call.
Because each protosocket runs as a protothread, the protosocket has to be started with a call to PSOCK_BEGIN() at the start of the function in which the protosocket is used. Similarly, the protosocket protothread can be terminated by a call to PSOCK_EXIT().
The example code below illustrates how to use the protosocket library. The program implements a simple SMTP client that sends a short email. The program is divided into two functions, one uIP event handler (smtp_uipcall()) and one function that runs the protosocket protothread and performs the SMTP communication (smtp_socketthread()).
An SMTP connection is represented by a smtp_state structure containing a struct psock and a small input buffer. The input buffer only needs to be 3 bytes long to accomodate the 3 byte status codes used by SMTP. Connection structures can be allocated from the memory buffer called connections, which is declared with the MEMB() macro.
The convenience macro SEND_STRING() is defined in order to simplify the code, as it mostly involves sending strings.
The function smtp_socketthread() is declared as a protothread using the PT_THREAD() macro. The PSOCK_BEGIN() call at the first line of the smtp_socketthread() function starts the protothread. SMTP specifies that the server will start with sending a welcome message that should include the status code 220 if the server is ready to accept messages. Therefore, the smtp_socketthread() first calls PSOCK_READTO() to read all incoming data up to the first newline. If the status code was anything else but 220, the protosocket is closed and the protosocket's protothread is terminated with the call to PSOCK_CLOSE_EXIT().
If the connection is accepted by the server, smtp_socketthread() continues with sending the HELO message. If this gets a positive reply (a status code beginning with a 2), the protothread moves on with the rest of the SMTP procedure. Finally, after all headers and data is sent, the program sends a QUIT before it finally closes the protosocket and exits the protosocket's protothread.
#include <string.h> #include "psock.h" #include "memb.h" struct smtp_state { struct psock psock; char inputbuffer[3]; }; MEMB(connections, sizeof(struct smtp_state), 2); #define SEND_STRING(s, str) PSOCK_SEND(s, str, strlen(str)) static PT_THREAD(smtp_socketthread(struct smtp_state *s)) { PSOCK_BEGIN(&s->psock); PSOCK_READTO(&s->psock, '\n'); if(strncmp(s->inputbuffer, "220", 3) != 0) { PSOCK_CLOSE_EXIT(&s->psock); } SEND_STRING(&s->psock, "HELO contiki.example.com\r\n"); PSOCK_READTO(&s->psock, '\n'); if(s->inputbuffer[0] != '2') { PSOCK_CLOSE_EXIT(&s->psock); } SEND_STRING(&s->psock, "MAIL FROM: contiki@example.com\r\n"); PSOCK_READTO(&s->psock, '\n'); if(s->inputbuffer[0] != '2') { PSOCK_CLOSE_EXIT(&s->psock); } SEND_STRING(&s->psock, "RCPT TO: contiki@example.com\r\n"); PSOCK_READTO(&s->psock, '\n'); if(s->inputbuffer[0] != '2') { PSOCK_CLOSE_EXIT(&s->psock); } SEND_STRING(&s->psock, "DATA\r\n"); PSOCK_READTO(&s->psock, '\n'); if(s->inputbuffer[0] != '3') { PSOCK_CLOSE_EXIT(&s->psock); } SEND_STRING(&s->psock, "To: contiki@example.com\r\n"); SEND_STRING(&s->psock, "From: contiki@example.com\r\n"); SEND_STRING(&s->psock, "Subject: Example\r\n"); SEND_STRING(&s->psock, "A test message from Contiki.\r\n"); SEND_STRING(&s->psock, "\r\n.\r\n"); PSOCK_READTO(&s->psock, '\n'); if(s->inputbuffer[0] != '2') { PSOCK_CLOSE_EXIT(&s->psock); } SEND_STRING(&s->psock, "QUIT\r\n"); PSOCK_END(&s->psock); } void smtp_uipcall(void *state) { struct smtp_state *s = (struct smtp_state *)state; if(uip_closed() || uip_aborted() || uip_timedout()) { memb_free(&connections, s); } else if(uip_connected()) { PSOCK_INIT(s, s->inputbuffer, sizeof(s->inputbuffer)); } else { smtp_socketthread(s); } }
Files | |
| file | psock.h |
| Protosocket library header file. | |
Data Structures | |
| struct | psock |
| The representation of a protosocket. More... | |
Defines | |
| #define | PSOCK_INIT(psock, buffer, buffersize) |
| Initialize a protosocket. | |
| #define | PSOCK_BEGIN(psock) |
| Start the protosocket protothread in a function. | |
| #define | PSOCK_SEND(psock, data, datalen) |
| Send data. | |
| #define | PSOCK_CLOSE(psock) |
| Close a protosocket. | |
| #define | PSOCK_READTO(psock, c) |
| Read data up to a specified character. | |
| #define | PSOCK_DATALEN(psock) |
| The length of the data that was previously read. | |
| #define | PSOCK_EXIT(psock) |
| Exit the protosocket's protothread. | |
| #define | PSOCK_CLOSE_EXIT(psock) |
| Close a protosocket and exit the protosocket's protothread. | |
| #define | PSOCK_NEWDATA(psock) |
| Check if new data has arrived on a protosocket. | |
| #define | PSOCK_WAIT_UNTIL(psock, condition) |
| Wait until a condition is true. | |
|
|
Start the protosocket protothread in a function. This macro starts the protothread associated with the protosocket and must come before other protosocket calls in the function it is used.
|
|
|
Close a protosocket. This macro closes a protosocket and can only be called from within the protothread in which the protosocket lives.
|
|
|
Close a protosocket and exit the protosocket's protothread. This macro closes a protosocket and exits the protosocket's protothread.
|
|
|
The length of the data that was previously read. This macro returns the length of the data that was previously read using PSOCK_READTO() or PSOCK_READ().
|
|
|
Exit the protosocket's protothread. This macro terminates the protothread of the protosocket and should almost always be used in conjunction with PSOCK_CLOSE().
|
|
|
Initialize a protosocket. This macro initializes a protosocket and must be called before the protosocket is used. The initialization also specifies the input buffer for the protosocket.
|
|
|
Check if new data has arrived on a protosocket. This macro is used in conjunction with the PSOCK_WAIT_UNTIL() macro to check if data has arrived on a protosocket.
|
|
|
Read data up to a specified character. This macro will block waiting for data and read the data into the input buffer specified with the call to PSOCK_INIT(). Data is only read until the specifieed character appears in the data stream.
|
|
|
Send data. This macro sends data over a protosocket. The protosocket protothread blocks until all data has been sent and is known to have been received by the remote end of the TCP connection.
|
|
|
Wait until a condition is true. This macro blocks the protothread until the specified condition is true. The macro PSOCK_NEWDATA() can be used to check if new data arrives when the protosocket is waiting. Typically, this macro is used as follows:
|
1.3.6