Merge pull request #620 from adamdunkels/push/socket-api

New TCP and UDP socket APIs
This commit is contained in:
Nicolas Tsiftes 2014-04-09 22:12:59 +02:00
commit 3829860c3c
8 changed files with 1153 additions and 0 deletions

View file

@ -54,6 +54,9 @@
#include "net/ip/psock.h" #include "net/ip/psock.h"
#include "net/ip/udp-socket.h"
#include "net/ip/tcp-socket.h"
#include "net/rime/rime.h" #include "net/rime/rime.h"
#include "net/netstack.h" #include "net/netstack.h"

365
core/net/ip/tcp-socket.c Normal file
View file

@ -0,0 +1,365 @@
/*
* Copyright (c) 2012-2014, Thingsquare, http://www.thingsquare.com/.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "contiki.h"
#include "contiki-net.h"
#include "lib/list.h"
#include "tcp-socket.h"
#include <stdio.h>
#include <string.h>
#define MIN(a, b) ((a) < (b) ? (a) : (b))
LIST(socketlist);
/*---------------------------------------------------------------------------*/
PROCESS(tcp_socket_process, "TCP socket process");
/*---------------------------------------------------------------------------*/
static void
call_event(struct tcp_socket *s, tcp_socket_event_t event)
{
if(s != NULL && s->event_callback != NULL) {
s->event_callback(s, s->ptr, event);
}
}
/*---------------------------------------------------------------------------*/
static void
senddata(struct tcp_socket *s)
{
int len;
if(s->output_data_len > 0) {
len = MIN(s->output_data_len, uip_mss());
s->output_data_send_nxt = len;
uip_send(s->output_data_ptr, len);
}
}
/*---------------------------------------------------------------------------*/
static void
acked(struct tcp_socket *s)
{
if(s->output_data_len > 0) {
/* Copy the data in the outputbuf down and update outputbufptr and
outputbuf_lastsent */
if(s->output_data_send_nxt > 0) {
memcpy(&s->output_data_ptr[0],
&s->output_data_ptr[s->output_data_send_nxt],
s->output_data_maxlen - s->output_data_send_nxt);
}
if(s->output_data_len < s->output_data_send_nxt) {
printf("tcp: acked assertion failed s->output_data_len (%d) < s->output_data_send_nxt (%d)\n",
s->output_data_len,
s->output_data_send_nxt);
}
s->output_data_len -= s->output_data_send_nxt;
s->output_data_send_nxt = 0;
call_event(s, TCP_SOCKET_DATA_SENT);
}
}
/*---------------------------------------------------------------------------*/
static void
newdata(struct tcp_socket *s)
{
uint16_t len, copylen, bytesleft;
uint8_t *dataptr;
len = uip_datalen();
dataptr = uip_appdata;
/* We have a segment with data coming in. We copy as much data as
possible into the input buffer and call the input callback
function. The input callback returns the number of bytes that
should be retained in the buffer, or zero if all data should be
consumed. If there is data to be retained, the highest bytes of
data are copied down into the input buffer. */
do {
copylen = MIN(len, s->input_data_maxlen);
memcpy(s->input_data_ptr, dataptr, copylen);
if(s->input_callback) {
bytesleft = s->input_callback(s, s->ptr,
s->input_data_ptr, copylen);
} else {
bytesleft = 0;
}
if(bytesleft > 0) {
printf("tcp: newdata, bytesleft > 0 (%d) not implemented\n", bytesleft);
}
dataptr += copylen;
len -= copylen;
} while(len > 0);
}
/*---------------------------------------------------------------------------*/
static void
relisten(struct tcp_socket *s)
{
if(s != NULL && s->listen_port != 0) {
s->flags |= TCP_SOCKET_FLAGS_LISTENING;
}
}
/*---------------------------------------------------------------------------*/
static void
appcall(void *state)
{
struct tcp_socket *s = state;
if(uip_connected()) {
/* Check if this connection originated in a local listen
socket. We do this by checking the state pointer - if NULL,
this is an incoming listen connection. If so, we need to
connect the socket to the uip_conn and call the event
function. */
if(s == NULL) {
for(s = list_head(socketlist);
s != NULL;
s = list_item_next(s)) {
if((s->flags & TCP_SOCKET_FLAGS_LISTENING) != 0 &&
s->listen_port != 0 &&
s->listen_port == uip_htons(uip_conn->lport)) {
s->flags &= ~TCP_SOCKET_FLAGS_LISTENING;
tcp_markconn(uip_conn, s);
call_event(s, TCP_SOCKET_CONNECTED);
break;
}
}
} else {
call_event(s, TCP_SOCKET_CONNECTED);
}
if(s == NULL) {
uip_abort();
} else {
if(uip_newdata()) {
newdata(s);
}
senddata(s);
}
return;
}
if(uip_timedout()) {
call_event(s, TCP_SOCKET_TIMEDOUT);
relisten(s);
}
if(uip_aborted()) {
call_event(s, TCP_SOCKET_ABORTED);
relisten(s);
}
if(s == NULL) {
uip_abort();
return;
}
if(uip_acked()) {
acked(s);
}
if(uip_newdata()) {
newdata(s);
}
if(uip_rexmit() ||
uip_newdata() ||
uip_acked()) {
senddata(s);
} else if(uip_poll()) {
senddata(s);
}
if(s->output_data_len == 0 && s->flags & TCP_SOCKET_FLAGS_CLOSING) {
s->flags &= ~TCP_SOCKET_FLAGS_CLOSING;
uip_close();
tcp_markconn(uip_conn, NULL);
call_event(s, TCP_SOCKET_CLOSED);
relisten(s);
}
if(uip_closed()) {
tcp_markconn(uip_conn, NULL);
call_event(s, TCP_SOCKET_CLOSED);
relisten(s);
}
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(tcp_socket_process, ev, data)
{
PROCESS_BEGIN();
while(1) {
PROCESS_WAIT_EVENT();
if(ev == tcpip_event) {
appcall(data);
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
static void
init(void)
{
static uint8_t inited = 0;
if(!inited) {
list_init(socketlist);
process_start(&tcp_socket_process, NULL);
inited = 1;
}
}
/*---------------------------------------------------------------------------*/
int
tcp_socket_register(struct tcp_socket *s, void *ptr,
uint8_t *input_databuf, int input_databuf_len,
uint8_t *output_databuf, int output_databuf_len,
tcp_socket_data_callback_t input_callback,
tcp_socket_event_callback_t event_callback)
{
init();
if(s == NULL) {
return -1;
}
s->ptr = ptr;
s->input_data_ptr = input_databuf;
s->input_data_maxlen = input_databuf_len;
s->output_data_ptr = output_databuf;
s->output_data_maxlen = output_databuf_len;
s->input_callback = input_callback;
s->event_callback = event_callback;
list_add(socketlist, s);
s->listen_port = 0;
s->flags = TCP_SOCKET_FLAGS_NONE;
return 1;
}
/*---------------------------------------------------------------------------*/
int
tcp_socket_connect(struct tcp_socket *s,
uip_ipaddr_t *ipaddr,
uint16_t port)
{
if(s == NULL) {
return -1;
}
PROCESS_CONTEXT_BEGIN(&tcp_socket_process);
s->c = tcp_connect(ipaddr, uip_htons(port), s);
PROCESS_CONTEXT_END();
if(s->c == NULL) {
return -1;
} else {
return 1;
}
}
/*---------------------------------------------------------------------------*/
int
tcp_socket_listen(struct tcp_socket *s,
uint16_t port)
{
if(s == NULL) {
return -1;
}
s->listen_port = port;
PROCESS_CONTEXT_BEGIN(&tcp_socket_process);
tcp_listen(uip_htons(port));
PROCESS_CONTEXT_END();
s->flags |= TCP_SOCKET_FLAGS_LISTENING;
return 1;
}
/*---------------------------------------------------------------------------*/
int
tcp_socket_unlisten(struct tcp_socket *s)
{
if(s == NULL) {
return -1;
}
PROCESS_CONTEXT_BEGIN(&tcp_socket_process);
tcp_unlisten(uip_htons(s->listen_port));
PROCESS_CONTEXT_END();
s->listen_port = 0;
s->flags &= ~TCP_SOCKET_FLAGS_LISTENING;
return 1;
}
/*---------------------------------------------------------------------------*/
int
tcp_socket_send(struct tcp_socket *s,
const uint8_t *data, int datalen)
{
int len;
if(s == NULL) {
return -1;
}
len = MIN(datalen, s->output_data_maxlen - s->output_data_len);
memcpy(&s->output_data_ptr[s->output_data_len], data, len);
s->output_data_len += len;
return len;
}
/*---------------------------------------------------------------------------*/
int
tcp_socket_send_str(struct tcp_socket *s,
const char *str)
{
return tcp_socket_send(s, (const uint8_t *)str, strlen(str));
}
/*---------------------------------------------------------------------------*/
int
tcp_socket_close(struct tcp_socket *s)
{
if(s == NULL) {
return -1;
}
s->flags |= TCP_SOCKET_FLAGS_CLOSING;
return 1;
}
/*---------------------------------------------------------------------------*/
int
tcp_socket_unregister(struct tcp_socket *s)
{
if(s == NULL) {
return -1;
}
tcp_socket_unlisten(s);
if(s->c != NULL) {
tcp_attach(s->c, NULL);
}
list_remove(socketlist, s);
return 1;
}
/*---------------------------------------------------------------------------*/

268
core/net/ip/tcp-socket.h Normal file
View file

@ -0,0 +1,268 @@
/*
* Copyright (c) 2012-2014, Thingsquare, http://www.thingsquare.com/.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef TCP_SOCKET_H
#define TCP_SOCKET_H
struct tcp_socket;
typedef enum {
TCP_SOCKET_CONNECTED,
TCP_SOCKET_CLOSED,
TCP_SOCKET_TIMEDOUT,
TCP_SOCKET_ABORTED,
TCP_SOCKET_DATA_SENT
} tcp_socket_event_t;
/**
* \brief TCP data callback function
* \param s A pointer to a TCP socket
* \param ptr A user-defined pointer
* \param input_data_ptr A pointer to the incoming data
* \param input_data_len The length of the incoming data
* \return The function should return the number of bytes to leave in the input buffer
*
* The TCP socket input callback function gets
* called whenever there is new data on the socket. The
* function can choose to either consume the data
* directly, or leave it in the buffer for later. The
* function must return the amount of data to leave in the
* buffer. I.e., if the callback function consumes all
* incoming data, it should return 0.
*/
typedef int (* tcp_socket_data_callback_t)(struct tcp_socket *s,
void *ptr,
const uint8_t *input_data_ptr,
int input_data_len);
/**
* \brief TCP event callback function
* \param s A pointer to a TCP socket
* \param ptr A user-defined pointer
* \param event The event number
*
* The TCP socket event callback function gets
* called whenever there is an event on a socket, such as
* the socket getting connected or closed.
*/
typedef void (* tcp_socket_event_callback_t)(struct tcp_socket *s,
void *ptr,
tcp_socket_event_t event);
struct tcp_socket {
struct tcp_socket *next;
tcp_socket_data_callback_t input_callback;
tcp_socket_event_callback_t event_callback;
void *ptr;
struct process *p;
uint8_t *input_data_ptr;
uint8_t *output_data_ptr;
uint16_t input_data_maxlen;
uint16_t input_data_len;
uint16_t output_data_maxlen;
uint16_t output_data_len;
uint16_t output_data_send_nxt;
uint8_t flags;
uint16_t listen_port;
struct uip_conn *c;
};
enum {
TCP_SOCKET_FLAGS_NONE = 0x00,
TCP_SOCKET_FLAGS_LISTENING = 0x01,
TCP_SOCKET_FLAGS_CLOSING = 0x02,
};
/**
* \brief Register a TCP socket
* \param s A pointer to a TCP socket
* \param ptr A user-defined pointer that will be sent to callbacks for this socket
* \param input_databuf A pointer to a memory area this socket will use for input data
* \param input_databuf_len The size of the input data buffer
* \param output_databuf A pointer to a memory area this socket will use for outgoing data
* \param output_databuf_len The size of the output data buffer
* \param data_callback A pointer to the data callback function for this socket
* \param event_callback A pointer to the event callback function for this socket
* \retval -1 If an error occurs
* \retval 1 If the operation succeeds.
*
* This function registers a TCP socket. The function sets
* up the output and input buffers for the socket and
* callback pointers.
*
* TCP sockets use input and output buffers for incoming
* and outgoing data. The memory for these buffers must be
* allocated by the caller. The size of the buffers
* determine the amount of data that can be received and
* sent, and the principle is that the application that
* sets up the TCP socket will know roughly how large
* these buffers should be. The rule of thumb is that the
* input buffer should be large enough to hold the largest
* application layer message that the application will
* receive and the output buffer should be large enough to
* hold the largest application layer message the
* application will send.
*
* TCP throttles incoming data so that if the input buffer
* is filled, the connection will halt until the
* application has read out the data from the input
* buffer.
*
*/
int tcp_socket_register(struct tcp_socket *s, void *ptr,
uint8_t *input_databuf, int input_databuf_len,
uint8_t *output_databuf, int output_databuf_len,
tcp_socket_data_callback_t data_callback,
tcp_socket_event_callback_t event_callback);
/**
* \brief Connect a TCP socket to a remote host
* \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
* \param ipaddr The IP address of the remote host
* \param port The TCP port number, in host byte order, of the remote host
* \retval -1 If an error occurs
* \retval 1 If the operation succeeds.
*
* This function connects a TCP socket to a remote host.
*
* When the socket has connected, the event callback will
* get called with the TCP_SOCKET_CONNECTED event. If the
* remote host does not accept the connection, the
* TCP_SOCKET_ABORTED will be sent to the callback. If the
* connection times out before conecting to the remote
* host, the TCP_SOCKET_TIMEDOUT event is sent to the
* callback.
*
*/
int tcp_socket_connect(struct tcp_socket *s,
uip_ipaddr_t *ipaddr,
uint16_t port);
/**
* \brief Start listening on a specific port
* \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
* \param port The TCP port number, in host byte order, of the remote host
* \retval -1 If an error occurs
* \retval 1 If the operation succeeds.
*
* This function causes the TCP socket to start listening
* on the given TCP port.
*
* Several sockets can listen on the same port. If a
* remote host connects to the port, one of the listening
* sockets will get connected and the event callback will
* be called with the TCP_SOCKET_CONNECTED event. When the
* connection closes, the socket will go back to listening
* for new connections.
*
*/
int tcp_socket_listen(struct tcp_socket *s,
uint16_t port);
/**
* \brief Stop listening for new connections
* \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
* \retval -1 If an error occurs
* \retval 1 If the operation succeeds.
*
* This function causes a listening TCP socket to stop
* listen. The socket must previously been put into listen
* mode with tcp_socket_listen().
*
*/
int tcp_socket_unlisten(struct tcp_socket *s);
/**
* \brief Send data on a connected TCP socket
* \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
* \param dataptr A pointer to the data to be sent
* \param datalen The length of the data to be sent
* \retval -1 If an error occurs
* \return The number of bytes that were successfully sent
*
* This function sends data over a connected TCP
* socket. The data is placed in the output buffer and
* sent to the remote host as soon as possiblce. When the
* data has been acknowledged by the remote host, the
* event callback is sent with the TCP_SOCKET_DATA_SENT
* event.
*/
int tcp_socket_send(struct tcp_socket *s,
const uint8_t *dataptr,
int datalen);
/**
* \brief Send a string on a connected TCP socket
* \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
* \param strptr A pointer to the string to be sent
* \retval -1 If an error occurs
* \return The number of bytes that were successfully sent
*
* This is a convenience function for sending strings on a
* TCP socket. The function calls tcp_socket_send() to
* send the string.
*/
int tcp_socket_send_str(struct tcp_socket *s,
const char *strptr);
/**
* \brief Close a connected TCP socket
* \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
* \retval -1 If an error occurs
* \retval 1 If the operation succeeds.
*
* This function closes a connected TCP socket. When the
* socket has been successfully closed, the event callback
* is called with the TCP_SOCKET_CLOSED event.
*
*/
int tcp_socket_close(struct tcp_socket *s);
/**
* \brief Unregister a registered socket
* \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
* \retval -1 If an error occurs
* \retval 1 If the operation succeeds.
*
* This function unregisters a previously registered
* socket. This must be done if the process will be
* unloaded from memory. If the TCP socket is connected,
* the connection will be reset.
*
*/
int tcp_socket_unregister(struct tcp_socket *s);
#endif /* TCP_SOCKET_H */

199
core/net/ip/udp-socket.c Normal file
View file

@ -0,0 +1,199 @@
/*
* Copyright (c) 2012-2014, Thingsquare, http://www.thingsquare.com/.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "contiki-net.h"
#include "udp-socket.h"
#include <string.h>
PROCESS(udp_socket_process, "UDP socket process");
static uint8_t buf[UIP_BUFSIZE];
#define UIP_IP_BUF ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN])
/*---------------------------------------------------------------------------*/
static void
init(void)
{
static uint8_t inited = 0;
if(!inited) {
inited = 1;
process_start(&udp_socket_process, NULL);
}
}
/*---------------------------------------------------------------------------*/
int
udp_socket_register(struct udp_socket *c,
void *ptr,
udp_socket_input_callback_t input_callback)
{
init();
if(c == NULL) {
return -1;
}
c->ptr = ptr;
c->input_callback = input_callback;
c->p = PROCESS_CURRENT();
PROCESS_CONTEXT_BEGIN(&udp_socket_process);
c->udp_conn = udp_new(NULL, 0, c);
PROCESS_CONTEXT_END();
if(c->udp_conn == NULL) {
return -1;
}
return 1;
}
/*---------------------------------------------------------------------------*/
int
udp_socket_close(struct udp_socket *c)
{
if(c == NULL) {
return -1;
}
if(c->udp_conn != NULL) {
uip_udp_remove(c->udp_conn);
return 1;
}
return -1;
}
/*---------------------------------------------------------------------------*/
int
udp_socket_bind(struct udp_socket *c,
uint16_t local_port)
{
if(c == NULL || c->udp_conn == NULL) {
return -1;
}
udp_bind(c->udp_conn, UIP_HTONS(local_port));
return 1;
}
/*---------------------------------------------------------------------------*/
int
udp_socket_connect(struct udp_socket *c,
uip_ipaddr_t *remote_addr,
uint16_t remote_port)
{
if(c == NULL || c->udp_conn == NULL) {
return -1;
}
if(remote_addr != NULL) {
uip_ipaddr_copy(&c->udp_conn->ripaddr, remote_addr);
}
c->udp_conn->rport = UIP_HTONS(remote_port);
return 1;
}
/*---------------------------------------------------------------------------*/
int
udp_socket_send(struct udp_socket *c,
const void *data, uint16_t datalen)
{
if(c == NULL || c->udp_conn == NULL) {
return -1;
}
uip_udp_packet_send(c->udp_conn, data, datalen);
return datalen;
}
/*---------------------------------------------------------------------------*/
int
udp_socket_sendto(struct udp_socket *c,
const void *data, uint16_t datalen,
const uip_ipaddr_t *to,
uint16_t port)
{
if(c == NULL || c->udp_conn == NULL) {
return -1;
}
if(c->udp_conn != NULL) {
uip_udp_packet_sendto(c->udp_conn, data, datalen,
to, UIP_HTONS(port));
return datalen;
}
return -1;
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(udp_socket_process, ev, data)
{
struct udp_socket *c;
PROCESS_BEGIN();
while(1) {
PROCESS_WAIT_EVENT();
if(ev == tcpip_event) {
/* An appstate pointer is passed to use from the IP stack
through the 'data' pointer. We registered this appstate when
we did the udp_new() call in udp_socket_register() as the
struct udp_socket pointer. So we extract this
pointer and use it when calling the reception callback. */
c = (struct udp_socket *)data;
/* Defensive coding: although the appstate *should* be non-null
here, we make sure to avoid the program crashing on us. */
if(c != NULL) {
/* If we were called because of incoming data, we should call
the reception callback. */
if(uip_newdata()) {
/* Copy the data from the uIP data buffer into our own
buffer to avoid the uIP buffer being messed with by the
callee. */
memcpy(buf, uip_appdata, uip_datalen());
/* Call the client process. We use the PROCESS_CONTEXT
mechanism to temporarily switch process context to the
client process. */
if(c->input_callback != NULL) {
PROCESS_CONTEXT_BEGIN(c->p);
c->input_callback(c, c->ptr,
&(UIP_IP_BUF->srcipaddr),
UIP_HTONS(UIP_IP_BUF->srcport),
&(UIP_IP_BUF->destipaddr),
UIP_HTONS(UIP_IP_BUF->destport),
buf, uip_datalen());
PROCESS_CONTEXT_END();
}
}
}
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
/** @} */

193
core/net/ip/udp-socket.h Normal file
View file

@ -0,0 +1,193 @@
/*
* Copyright (c) 2012-2014, Thingsquare, http://www.thingsquare.com/.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef UDP_SOCKET_H
#define UDP_SOCKET_H
#include "net/ip/uip.h"
struct udp_socket;
/**
* \brief A UDP socket callback function
* \param c A pointer to the struct udp_socket that received the data
* \param ptr An opaque pointer that was specified when the UDP socket was registered with udp_socket_register()
* \param source_addr The IP address from which the datagram was sent
* \param source_port The UDP port number, in host byte order, from which the datagram was sent
* \param dest_addr The IP address that this datagram was sent to
* \param dest_port The UDP port number, in host byte order, that the datagram was sent to
* \param data A pointer to the data contents of the UDP datagram
* \param datalen The length of the data being pointed to by the data pointer
*
* Each UDP socket has a callback function that is
* registered as part of the call to
* udp_socket_register(). The callback function gets
* called every time a UDP packet is received.
*/
typedef void (* udp_socket_input_callback_t)(struct udp_socket *c,
void *ptr,
const uip_ipaddr_t *source_addr,
uint16_t source_port,
const uip_ipaddr_t *dest_addr,
uint16_t dest_port,
const uint8_t *data,
uint16_t datalen);
struct udp_socket {
udp_socket_input_callback_t input_callback;
void *ptr;
struct process *p;
struct uip_udp_conn *udp_conn;
};
/**
* \brief Register a UDP socket
* \param c A pointer to the struct udp_socket that should be registered
* \param ptr An opaque pointer that will be passed to callbacks
* \param receive_callback A function pointer to the callback function that will be called when data arrives
* \retval -1 The registration failed
* \retval 1 The registration succeeded
*
* This function registers the UDP socket with the
* system. A UDP socket must be registered before any data
* can be sent or received over the socket.
*
* The caller must allocate memory for the struct
* udp_socket that is to be registered.
*
* A UDP socket can begin to receive data by calling
* udp_socket_bind().
*
*/
int udp_socket_register(struct udp_socket *c,
void *ptr,
udp_socket_input_callback_t receive_callback);
/**
* \brief Bind a UDP socket to a local port
* \param c A pointer to the struct udp_socket that should be bound to a local port
* \param local_port The UDP port number, in host byte order, to bind the UDP socket to
* \retval -1 Binding the UDP socket to the local port failed
* \retval 1 Binding the UDP socket to the local port succeeded
*
* This function binds the UDP socket to a local port so
* that it will begin to receive data that arrives on the
* specified port. A UDP socket will receive data
* addressed to the specified port number on any IP
* address of the host.
*
* A UDP socket that is bound to a local port will use
* this port number as a source port in outgoing UDP
* messages.
*
*/
int udp_socket_bind(struct udp_socket *c,
uint16_t local_port);
/**
* \brief Bind a UDP socket to a remote address and port
* \param c A pointer to the struct udp_socket that should be connected
* \param remote_addr The IP address of the remote host, or NULL if the UDP socket should only be connected to a specific port
* \param remote_port The UDP port number, in host byte order, to which the UDP socket should be connected
* \retval -1 Connecting the UDP socket failed
* \retval 1 Connecting the UDP socket succeeded
*
* This function connects the UDP socket to a specific
* remote port and optional remote IP address. When a UDP
* socket is connected to a remote port and address, it
* will only receive packets that are sent from the remote
* port and address. When sending data over a connected
* UDP socket, the data will be sent to the connected
* remote address.
*
* A UDP socket can be connected to a remote port, but not
* a remote IP address, by providing a NULL parameter as
* the remote_addr parameter. This lets the UDP socket
* receive data from any IP address on the specified port.
*
*/
int udp_socket_connect(struct udp_socket *c,
uip_ipaddr_t *remote_addr,
uint16_t remote_port);
/**
* \brief Send data on a UDP socket
* \param c A pointer to the struct udp_socket on which the data should be sent
* \param data A pointer to the data that should be sent
* \param datalen The length of the data to be sent
* \return The number of bytes sent, or -1 if an error occurred
*
* This function sends data over a UDP socket. The UDP
* socket must have been connected to a remote address and
* port with udp_socket_connect().
*
*/
int udp_socket_send(struct udp_socket *c,
const void *data, uint16_t datalen);
/**
* \brief Send data on a UDP socket to a specific address and port
* \param c A pointer to the struct udp_socket on which the data should be sent
* \param data A pointer to the data that should be sent
* \param datalen The length of the data to be sent
* \param addr The IP address to which the data should be sent
* \param port The UDP port number, in host byte order, to which the data should be sent
* \return The number of bytes sent, or -1 if an error occurred
*
* This function sends data over a UDP socket to a
* specific address and port.
*
* The UDP socket does not have to be connected to use
* this function.
*
*/
int udp_socket_sendto(struct udp_socket *c,
const void *data, uint16_t datalen,
const uip_ipaddr_t *addr, uint16_t port);
/**
* \brief Close a UDP socket
* \param c A pointer to the struct udp_socket to be closed
* \retval -1 If closing the UDP socket failed
* \retval 1 If closing the UDP socket succeeded
*
* This function closes a UDP socket that has previously
* been registered with udp_socket_register(). All
* registered UDP sockets must be closed before exiting
* the process that registered them, or undefined behavior
* may occur.
*
*/
int udp_socket_close(struct udp_socket *c);
#endif /* UDP_SOCKET_H */

View file

@ -0,0 +1,5 @@
all: tcp-server
CONTIKI=../..
include $(CONTIKI)/Makefile.include

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2012, Thingsquare, http://www.thingsquare.com/.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "contiki-net.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SERVER_PORT 80
static struct tcp_socket socket;
#define INPUTBUFSIZE 400
static uint8_t inputbuf[INPUTBUFSIZE];
#define OUTPUTBUFSIZE 400
static uint8_t outputbuf[OUTPUTBUFSIZE];
PROCESS(tcp_server_process, "TCP echo process");
AUTOSTART_PROCESSES(&tcp_server_process);
static uint8_t get_received;
static int bytes_to_send;
/*---------------------------------------------------------------------------*/
static int
input(struct tcp_socket *s, void *ptr,
const uint8_t *inputptr, int inputdatalen)
{
printf("input %d bytes '%s'\n", inputdatalen, inputptr);
if(!get_received) {
/* See if we have a full GET request in the buffer. */
if(strncmp((char *)inputptr, "GET /", 5) == 0 &&
atoi((char *)&inputptr[5]) != 0) {
bytes_to_send = atoi((char *)&inputptr[5]);
printf("bytes_to_send %d\n", bytes_to_send);
return 0;
}
printf("inputptr '%.*s'\n", inputdatalen, inputptr);
/* Return the number of data bytes we received, to keep them all
in the buffer. */
return inputdatalen;
} else {
/* Discard everything */
return 0; /* all data consumed */
}
}
/*---------------------------------------------------------------------------*/
static void
event(struct tcp_socket *s, void *ptr,
tcp_socket_event_t ev)
{
printf("event %d\n", ev);
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(tcp_server_process, ev, data)
{
PROCESS_BEGIN();
tcp_socket_register(&socket, NULL,
inputbuf, sizeof(inputbuf),
outputbuf, sizeof(outputbuf),
input, event);
tcp_socket_listen(&socket, SERVER_PORT);
printf("Listening on %d\n", SERVER_PORT);
while(1) {
PROCESS_PAUSE();
if(bytes_to_send > 0) {
/* Send header */
printf("sending header\n");
tcp_socket_send_str(&socket, "HTTP/1.0 200 ok\r\nServer: Contiki tcp-socket example\r\n\r\n");
/* Send data */
printf("sending data\n");
while(bytes_to_send > 0) {
PROCESS_PAUSE();
int len, tosend;
#define MIN(a,b) ((a)<(b)?(a):(b))
tosend = MIN(bytes_to_send, sizeof(outputbuf));
len = tcp_socket_send(&socket, (uint8_t *)"", tosend);
bytes_to_send -= len;
}
tcp_socket_close(&socket);
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/

View file

@ -27,6 +27,7 @@ sky-ip/sky \
sky-shell/sky \ sky-shell/sky \
sky-shell-exec/sky \ sky-shell-exec/sky \
sky-shell-webserver/sky \ sky-shell-webserver/sky \
tcp-socket/minimal-net \
telnet-server/minimal-net \ telnet-server/minimal-net \
webserver/minimal-net \ webserver/minimal-net \
webserver-ipv6/exp5438 \ webserver-ipv6/exp5438 \