X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=bertos%2Fnet%2Ftcp_socket.c;fp=bertos%2Fnet%2Ftcp_socket.c;h=1924a60495b1d48777cac0588b1633866541f2d5;hb=121f4d98bd4d74629809697ab66001ebcbac76ce;hp=0000000000000000000000000000000000000000;hpb=fb5863ca8d0db3ff2e84721f7c902b031157ebb0;p=bertos.git diff --git a/bertos/net/tcp_socket.c b/bertos/net/tcp_socket.c new file mode 100644 index 00000000..1924a604 --- /dev/null +++ b/bertos/net/tcp_socket.c @@ -0,0 +1,214 @@ +/** + * \file + * + * + * \brief TCP sockect with kfile interface. + * + * \author Luca Ottaviano + * \author Daniele Basile + */ + +#include "tcp_socket.h" + +#define LOG_LEVEL LOG_LVL_WARN +#define LOG_FORMAT LOG_FMT_TERSE +#include +#include + +#include +#include +#include +#include + +static int tcpConnect(TcpSocket *socket) +{ + socket->sock = netconn_new(NETCONN_TCP); + ASSERT(socket->sock); + + if(netconn_bind(socket->sock, socket->local_addr, socket->port) != ERR_OK) + { + LOG_ERR("Connection error\n"); + goto error; + } + + if(netconn_connect(socket->sock, socket->remote_addr, socket->port) != ERR_OK) + { + LOG_ERR("Cannot create socket\n"); + goto error; + } + + LOG_INFO("connected ip=%d.%d.%d.%d\n", IP_ADDR_TO_INT_TUPLE(socket->local_addr->addr)); + return 0; + +error: + netconn_delete(socket->sock); + socket->sock = NULL; + return -1; +} + + +static bool reconnect(TcpSocket *socket) +{ + LOG_INFO("Reconnecting...\n"); + // Release old socket if needed + if (socket->sock) + { + if (netconn_delete(socket->sock) != ERR_OK) + LOG_ERR("Error closing socket\n"); + + socket->sock = NULL; + } + + // Connect to our peer peer + if (tcpConnect(socket) < 0) + { + LOG_ERR("Reconnect error!\n"); + socket->error |= ERR_TCP_NOTCONN; + return false; + } + + LOG_INFO("Reconnecting DONE!\n"); + + return true; +} + +static int tcpsocket_close(KFile *fd) +{ + TcpSocket *socket = TCPSOCKET_CAST(fd); + int ret = netconn_delete(socket->sock); + socket->sock = NULL; + + if (ret) + { + LOG_ERR("Close error\n"); + socket->error |= ERR_CONN_CLOSE; + return EOF; + } + return 0; +} + +static size_t tcpsocket_read(KFile *fd, void *buf, size_t len) +{ + TcpSocket *socket = TCPSOCKET_CAST(fd); + uint16_t recv_len = 0; + + // Try reconnecting if our socket isn't valid + if (!socket->sock) + { + if (!reconnect(socket)) + return 0; + } + + while (len) + { + struct netbuf *rx_buf_conn = netconn_recv(socket->sock); + if (rx_buf_conn) + { + netbuf_data(rx_buf_conn, (void **)&buf, &recv_len); + + socket->error |= ERR_RECV_DATA; + return recv_len; + } + + // Do we have an EOF condition? If so, bailout. + if (recv_len == 0 && len != 0) + { + LOG_INFO("Connection reset by peer\n"); + socket->error = 0; + + if (tcpsocket_close(fd) == EOF) + LOG_ERR("Error closing socket, leak detected\n"); + + return recv_len; + } + len -= recv_len; + } + + return recv_len; +} + +static size_t tcpsocket_write(KFile *fd, const void *buf, size_t len) +{ + TcpSocket *socket = TCPSOCKET_CAST(fd); + ssize_t result; + + // Try reconnecting if our socket isn't valid + if (!socket->sock) + { + if (!reconnect(socket)) + return 0; + } + + result = netconn_write(socket->sock, buf, len, NETCONN_COPY); + if (result != ERR_OK) + { + LOG_ERR("While writing %d\n", result); + if (result == ERR_RST) + { + LOG_INFO("Connection close\n"); + + if (tcpsocket_close(fd) == EOF) + LOG_ERR("Error closing socket, leak detected\n"); + return 0; + } + return 0; + } + + return len; +} + +static int tcpsocket_error(KFile *fd) +{ + TcpSocket *socket = TCPSOCKET_CAST(fd); + return socket->error; +} + +static void tcpsocket_clearerr(KFile *fd) +{ + TcpSocket *socket = TCPSOCKET_CAST(fd); + socket->error = 0; +} + +void tcpsocket_init(TcpSocket *socket, struct ip_addr *local_addr, struct ip_addr *remote_addr, uint16_t port) +{ + socket->sock = NULL; + socket->local_addr = local_addr; + socket->remote_addr = remote_addr; + socket->port = port; + + socket->fd._type = KFT_TCPSOCKET; + socket->fd.read = tcpsocket_read; + socket->fd.error = tcpsocket_error; + socket->fd.close = tcpsocket_close; + socket->fd.write = tcpsocket_write; + socket->fd.clearerr = tcpsocket_clearerr; + +}