Mbed library for ENC28J60 Ethernet modules. Full support for TCP/IP and UDP Server, Client and HTTP server (webserver). DHCP and DNS is included.

Dependents:   mBuino_ENC28_MQTT Nucleo_Web_ENC28J60 Nucleo_Web_ENC28J60_ADC Serial_over_Ethernet ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers TcpClient.cpp Source File

TcpClient.cpp

00001 /*
00002  UIPClient.cpp - Arduino implementation of a UIP wrapper class.
00003  Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
00004  All rights reserved.
00005 
00006  Modified (ported to mbed) by Zoltan Hudak <hudakz@inbox.com>
00007 
00008  This program is free software: you can redistribute it and/or modify
00009  it under the terms of the GNU General Public License as published by
00010  the Free Software Foundation, either version 3 of the License, or
00011  (at your option) any later version.
00012 
00013  This program is distributed in the hope that it will be useful,
00014  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  GNU General Public License for more details.
00017 
00018  You should have received a copy of the GNU General Public License
00019  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00020  */
00021 extern "C"
00022 {
00023 #include "utility/uip-conf.h"
00024 #include "utility/uip.h"
00025 #include "utility/uip_arp.h"
00026 #include "string.h"
00027 }
00028 #include "UipEthernet.h"
00029 #include "TcpClient.h"
00030 #include "DnsClient.h"
00031 
00032 #define UIP_TCP_PHYH_LEN    UIP_LLH_LEN + UIP_IPTCPH_LEN
00033 
00034 uip_userdata_t TcpClient::all_data[UIP_CONNS];
00035 
00036 /**
00037  * @brief
00038  * @note
00039  * @param
00040  * @retval
00041  */
00042 TcpClient::TcpClient() :
00043     data(NULL),
00044     _instance(NULL)
00045 { }
00046 
00047 /**
00048  * @brief
00049  * @note
00050  * @param
00051  * @retval
00052  */
00053 TcpClient::TcpClient(uip_userdata_t* conn_data) :
00054     data(conn_data),
00055     _instance(NULL)
00056 { }
00057 
00058 /**
00059  * @brief
00060  * @note
00061  * @param
00062  * @retval
00063  */
00064 int TcpClient::open(UipEthernet* ethernet)
00065 {
00066     if (UipEthernet::ethernet != ethernet)
00067         UipEthernet::ethernet = ethernet;
00068 
00069     return 0;
00070 }
00071 
00072 /**
00073  * @brief
00074  * @note
00075  * @param
00076  * @retval
00077  */
00078 int TcpClient::connect(IpAddress ip, uint16_t port)
00079 {
00080     stop();
00081 
00082     uip_ipaddr_t    ipaddr;
00083     uip_ip_addr(ipaddr, ip);
00084 
00085     struct uip_conn*    conn = uip_connect(&ipaddr, htons(port));
00086     if (conn)
00087     {
00088 #if UIP_CONNECT_TIMEOUT > 0
00089         int32_t timeout = time(NULL) + UIP_CONNECT_TIMEOUT;
00090 #endif
00091         while ((conn->tcpstateflags & UIP_TS_MASK) != UIP_CLOSED) {
00092             UipEthernet::ethernet->tick();
00093             if ((conn->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED) {
00094                 data = (uip_userdata_t*)conn->appstate;
00095 #ifdef UIPETHERNET_DEBUG_CLIENT
00096                 printf("connected, state: %d, first packet in: %d\r\n", data->state, data->packets_in[0]);
00097 #endif
00098                 return 0;
00099             }
00100 
00101 #if UIP_CONNECT_TIMEOUT > 0
00102             if (((int32_t) (time(NULL) - timeout)) > 0) {
00103                 conn->tcpstateflags = UIP_CLOSED;
00104                 break;
00105             }
00106 #endif
00107         }
00108     }
00109 
00110     return 1;
00111 }
00112 
00113 /**
00114  * @brief
00115  * @note
00116  * @param
00117  * @retval
00118  */
00119 int TcpClient::connect(const char* host, uint16_t port)
00120 {
00121     // Look up the host first
00122     int         ret = 0;
00123 #if UIP_UDP
00124     DnsClient   dns;
00125     IpAddress   remote_addr;
00126 
00127     dns.begin(UipEthernet::dnsServerAddress);
00128     ret = dns.getHostByName(host, remote_addr);
00129     if (ret == 1) {
00130         return connect(remote_addr, port);
00131     }
00132 #endif
00133     return ret;
00134 }
00135 
00136 /**
00137  * @brief
00138  * @note
00139  * @param
00140  * @retval
00141  */
00142 void TcpClient::stop()
00143 {
00144     if (data && data->state)
00145     {
00146 #ifdef UIPETHERNET_DEBUG_CLIENT
00147         printf("before stop(), with data\r\n");
00148         _dumpAllData();
00149 #endif
00150         _flushBlocks(&data->packets_in[0]);
00151         if (data->state & UIP_CLIENT_REMOTECLOSED) {
00152             data->state = 0;
00153         }
00154         else {
00155             data->state |= UIP_CLIENT_CLOSE;
00156         }
00157 
00158 #ifdef UIPETHERNET_DEBUG_CLIENT
00159         printf("after stop()\r\n");
00160         _dumpAllData();
00161 #endif
00162     }
00163 
00164 #ifdef UIPETHERNET_DEBUG_CLIENT
00165     else {
00166         printf("stop(), data: NULL\r\n");
00167     }
00168 #endif
00169     data = NULL;
00170     UipEthernet::ethernet->tick();
00171 }
00172 
00173 /**
00174  * @brief
00175  * @note
00176  * @param
00177  * @retval
00178  */
00179 uint8_t TcpClient::connected()
00180 {
00181     return(data && (data->packets_in[0] != NOBLOCK || (data->state & UIP_CLIENT_CONNECTED))) ? 1 : 0;
00182 }
00183 
00184 /**
00185  * @brief
00186  * @note
00187  * @param
00188  * @retval
00189  */
00190 void TcpClient::setInstance(TcpClient *client)
00191 {
00192     _instance = client;
00193 }
00194 
00195 /**
00196  * @brief
00197  * @note
00198  * @param
00199  * @retval
00200  */
00201 bool TcpClient::operator==(const TcpClient& rhs)
00202 {
00203     return data && rhs.data && (data == rhs.data);
00204 }
00205 
00206 /**
00207  * @brief
00208  * @note
00209  * @param
00210  * @retval
00211  */
00212 TcpClient::operator bool()
00213 {
00214     UipEthernet::ethernet->tick();
00215     return data && (!(data->state & UIP_CLIENT_REMOTECLOSED) || data->packets_in[0] != NOBLOCK);
00216 }
00217 
00218 /**
00219  * @brief
00220  * @note
00221  * @param
00222  * @retval
00223  */
00224 int TcpClient::send(uint8_t c)
00225 {
00226     return _write(data, &c, 1);
00227 }
00228 
00229 /**
00230  * @brief
00231  * @note
00232  * @param
00233  * @retval
00234  */
00235 int TcpClient::send(const uint8_t* buf, size_t size)
00236 {
00237     return _write(data, buf, size);
00238 }
00239 
00240 /**
00241  * @brief
00242  * @note
00243  * @param
00244  * @retval
00245  */
00246 int TcpClient::_write(uip_userdata_t* data, const uint8_t* buf, size_t size)
00247 {
00248     size_t      remain = size;
00249     uint16_t    written;
00250 #if UIP_ATTEMPTS_ON_WRITE > 0
00251     uint16_t    attempts = UIP_ATTEMPTS_ON_WRITE;
00252 #endif
00253     repeat : UipEthernet::ethernet->tick();
00254     if (data && !(data->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))) {
00255         uint8_t p = _currentBlock(&data->packets_out[0]);
00256         if (data->packets_out[p] == NOBLOCK)
00257         {
00258 newpacket:
00259             data->packets_out[p] = UipEthernet::ethernet->enc28j60Eth.allocBlock(UIP_SOCKET_DATALEN);
00260             if (data->packets_out[p] == NOBLOCK)
00261             {
00262 #if UIP_ATTEMPTS_ON_WRITE > 0
00263                 if (--attempts > 0)
00264 #endif
00265 #if UIP_ATTEMPTS_ON_WRITE != 0
00266                     goto repeat;
00267 #endif
00268                 goto ready;
00269             }
00270 
00271             data->out_pos = 0;
00272         }
00273 
00274 #ifdef UIPETHERNET_DEBUG_CLIENT
00275         printf
00276         (
00277             "UIPClient.write: writePacket(%d) pos: %d, buf[%d-%d]\r\n",
00278             u->packets_out[p],
00279             u->out_pos,
00280             size - remain,
00281             remain
00282         );
00283 #endif
00284         written = UipEthernet::ethernet->enc28j60Eth.writePacket
00285             (
00286                 data->packets_out[p],
00287                 data->out_pos,
00288                 (uint8_t*)buf + size - remain,
00289                 remain
00290             );
00291         remain -= written;
00292         data->out_pos += written;
00293         if (remain > 0) {
00294             if (p == UIP_SOCKET_NUMPACKETS - 1)
00295             {
00296 #if UIP_ATTEMPTS_ON_WRITE > 0
00297                 if (--attempts > 0)
00298 #endif
00299 #if UIP_ATTEMPTS_ON_WRITE != 0
00300                     goto repeat;
00301 #endif
00302                 goto ready;
00303             }
00304 
00305             p++;
00306             goto newpacket;
00307         }
00308 
00309 ready:
00310         data->pollTimer.start();
00311         return size - remain;
00312     }
00313 
00314     return -1;
00315 }
00316 
00317 /**
00318  * @brief
00319  * @note
00320  * @param
00321  * @retval
00322  */
00323 size_t TcpClient::available()
00324 {
00325     if (*this)
00326         return _available(data);
00327     return 0;
00328 }
00329 
00330 /**
00331  * @brief
00332  * @note
00333  * @param
00334  * @retval
00335  */
00336 size_t TcpClient::_available(uip_userdata_t* u)
00337 {
00338     size_t  len = 0;
00339     for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) {
00340         len += UipEthernet::ethernet->enc28j60Eth.blockSize(u->packets_in[i]);
00341     }
00342 
00343     return len;
00344 }
00345 
00346 /**
00347  * @brief
00348  * @note
00349  * @param
00350  * @retval
00351  */
00352 int TcpClient::recv(uint8_t* buf, size_t size)
00353 {
00354     if (*this) {
00355         uint16_t    remain = size;
00356         if (data->packets_in[0] == NOBLOCK)
00357             return 0;
00358 
00359         uint16_t    read;
00360         do {
00361             read = UipEthernet::ethernet->enc28j60Eth.readPacket(data->packets_in[0], 0, buf + size - remain, remain);
00362             if (read == UipEthernet::ethernet->enc28j60Eth.blockSize(data->packets_in[0])) {
00363                 remain -= read;
00364                 _eatBlock(&data->packets_in[0]);
00365                 if
00366                 (
00367                     uip_stopped(&uip_conns[data->state & UIP_CLIENT_SOCKETS]) &&
00368                     !(data->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))
00369                 ) data->state |= UIP_CLIENT_RESTART;
00370                 if (data->packets_in[0] == NOBLOCK) {
00371                     if (data->state & UIP_CLIENT_REMOTECLOSED) {
00372                         data->state = 0;
00373                         data = NULL;
00374                     }
00375 
00376                     return size - remain;
00377                 }
00378             }
00379             else {
00380                 UipEthernet::ethernet->enc28j60Eth.resizeBlock(data->packets_in[0], read);
00381                 break;
00382             }
00383         } while (remain > 0);
00384         return size;
00385     }
00386 
00387     return -1;
00388 }
00389 
00390 /**
00391  * @brief
00392  * @note
00393  * @param
00394  * @retval
00395  */
00396 int TcpClient::recv()
00397 {
00398     static uint8_t  c;
00399     if (recv(&c, 1) < 0)
00400         return -1;
00401     return c;
00402 }
00403 
00404 /**
00405  * @brief
00406  * @note
00407  * @param
00408  * @retval
00409  */
00410 int TcpClient::peek()
00411 {
00412     static uint8_t  c;
00413     if (*this) {
00414         if (data->packets_in[0] != NOBLOCK) {
00415             UipEthernet::ethernet->enc28j60Eth.readPacket(data->packets_in[0], 0, &c, 1);
00416             return c;
00417         }
00418     }
00419 
00420     return -1;
00421 }
00422 
00423 /**
00424  * @brief
00425  * @note
00426  * @param
00427  * @retval
00428  */
00429 void TcpClient::flush()
00430 {
00431     if (*this) {
00432         _flushBlocks(&data->packets_in[0]);
00433     }
00434 }
00435 
00436 /**
00437  * @brief
00438  * @note
00439  * @param
00440  * @retval
00441  */
00442 IpAddress TcpClient::getRemoteIp()
00443 {
00444     return ip_addr_uip(data->ripaddr);
00445 }
00446 
00447 /**
00448  * @brief
00449  * @note
00450  * @param
00451  * @retval
00452  */
00453 const char* TcpClient::getpeername()
00454 {
00455     static char buf[16];
00456 
00457     return getRemoteIp().toString(buf);
00458 }
00459 
00460 /**
00461  * @brief
00462  * @note
00463  * @param
00464  * @retval
00465  */
00466 void TcpClient::close()
00467 {
00468     stop();
00469     if (_instance)
00470         delete this;
00471 }
00472 
00473 /**
00474  * @brief
00475  * @note
00476  * @param
00477  * @retval
00478  */
00479 void uipclient_appcall()
00480 {
00481     uint16_t            send_len = 0;
00482     uip_userdata_t*     u = (uip_userdata_t*)uip_conn->appstate;
00483     if (!u && uip_connected())
00484     {
00485 #ifdef UIPETHERNET_DEBUG_CLIENT
00486         printf("UIPClient uip_connected\r\n");
00487         UIPClient::_dumpAllData();
00488 #endif
00489         u = (uip_userdata_t*)TcpClient::_allocateData();
00490         if (u) {
00491             uip_conn->appstate = u;
00492 #ifdef UIPETHERNET_DEBUG_CLIENT
00493             printf("UIPClient allocated state: %d", u->state);
00494 #endif
00495         }
00496 
00497 #ifdef UIPETHERNET_DEBUG_CLIENT
00498         else
00499             printf("UIPClient allocation failed\r\n");
00500 #endif
00501     }
00502 
00503     if (u) {
00504         if (uip_newdata())
00505         {
00506 #ifdef UIPETHERNET_DEBUG_CLIENT
00507             printf("UIPClient uip_newdata, uip_len: %d\r\n", uip_len);
00508 #endif
00509             if (uip_len && !(u->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))) {
00510                 for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) {
00511                     if (u->packets_in[i] == NOBLOCK) {
00512                         u->packets_in[i] = UipEthernet::ethernet->enc28j60Eth.allocBlock(uip_len);
00513                         if (u->packets_in[i] != NOBLOCK) {
00514                             UipEthernet::ethernet->enc28j60Eth.copyPacket
00515                                 (
00516                                     u->packets_in[i],
00517                                     0,
00518                                     UipEthernet::inPacket,
00519                                     ((uint8_t*)uip_appdata) - uip_buf,
00520                                     uip_len
00521                                 );
00522                             if (i == UIP_SOCKET_NUMPACKETS - 1)
00523                                 uip_stop();
00524                             goto finish_newdata;
00525                         }
00526                     }
00527                 }
00528 
00529                 UipEthernet::packetState &= ~UIPETHERNET_FREEPACKET;
00530                 uip_stop();
00531             }
00532         }
00533 
00534 finish_newdata:
00535         if (u->state & UIP_CLIENT_RESTART) {
00536             u->state &= ~UIP_CLIENT_RESTART;
00537             uip_restart();
00538         }
00539 
00540         // If the connection has been closed, save received but unread data.
00541         if (uip_closed() || uip_timedout())
00542         {
00543 #ifdef UIPETHERNET_DEBUG_CLIENT
00544             printf("UIPClient uip_closed\r\n");
00545             UIPClient::_dumpAllData();
00546 #endif
00547             // drop outgoing packets not sent yet:
00548 
00549             TcpClient::_flushBlocks(&u->packets_out[0]);
00550             if (u->packets_in[0] != NOBLOCK) {
00551                 ((uip_userdata_closed_t*)u)->lport = uip_conn->lport;
00552                 u->state |= UIP_CLIENT_REMOTECLOSED;
00553             }
00554             else
00555                 u->state = 0;
00556 
00557             // disassociate appdata.
00558 #ifdef UIPETHERNET_DEBUG_CLIENT
00559             printf("after UIPClient uip_closed\r\n");
00560             UIPClient::_dumpAllData();
00561 #endif
00562             uip_conn->appstate = NULL;
00563             goto finish;
00564         }
00565 
00566         if (uip_acked())
00567         {
00568 #ifdef UIPETHERNET_DEBUG_CLIENT
00569             printf("UIPClient uip_acked\r\n");
00570 #endif
00571             TcpClient::_eatBlock(&u->packets_out[0]);
00572         }
00573 
00574         if (uip_poll() || uip_rexmit())
00575         {
00576 #ifdef UIPETHERNET_DEBUG_CLIENT
00577             //printf("UIPClient uip_poll\r\n");
00578 #endif
00579             if (u->packets_out[0] != NOBLOCK) {
00580                 if (u->packets_out[1] == NOBLOCK) {
00581                     send_len = u->out_pos;
00582                     if (send_len > 0) {
00583                         UipEthernet::ethernet->enc28j60Eth.resizeBlock(u->packets_out[0], 0, send_len);
00584                     }
00585                 }
00586                 else
00587                     send_len = UipEthernet::ethernet->enc28j60Eth.blockSize(u->packets_out[0]);
00588                 if (send_len > 0) {
00589                     UipEthernet::uipHeaderLen = ((uint8_t*)uip_appdata) - uip_buf;
00590                     UipEthernet::uipPacket = UipEthernet::ethernet->enc28j60Eth.allocBlock(UipEthernet::uipHeaderLen + send_len);
00591                     if (UipEthernet::uipPacket != NOBLOCK) {
00592                         UipEthernet::ethernet->enc28j60Eth.copyPacket
00593                             (
00594                                 UipEthernet::uipPacket,
00595                                 UipEthernet::uipHeaderLen,
00596                                 u->packets_out[0],
00597                                 0,
00598                                 send_len
00599                             );
00600                         UipEthernet::packetState |= UIPETHERNET_SENDPACKET;
00601                     }
00602                 }
00603 
00604                 goto finish;
00605             }
00606         }
00607 
00608         // don't close connection unless all outgoing packets are sent
00609         if (u->state & UIP_CLIENT_CLOSE)
00610         {
00611 #ifdef UIPETHERNET_DEBUG_CLIENT
00612             printf("UIPClient state UIP_CLIENT_CLOSE\r\n");
00613             UIPClient::_dumpAllData();
00614 #endif
00615             if (u->packets_out[0] == NOBLOCK) {
00616                 u->state = 0;
00617                 uip_conn->appstate = NULL;
00618                 uip_close();
00619 #ifdef UIPETHERNET_DEBUG_CLIENT
00620                 printf("no blocks out -> free userdata\r\n");
00621                 UIPClient::_dumpAllData();
00622 #endif
00623             }
00624             else {
00625                 uip_stop();
00626 #ifdef UIPETHERNET_DEBUG_CLIENT
00627                 printf("blocks outstanding transfer -> uip_stop()\r\n");
00628 #endif
00629             }
00630         }
00631     }
00632 
00633 finish:
00634     uip_send(uip_appdata, send_len);
00635     uip_len = send_len;
00636 }
00637 
00638 /**
00639  * @brief
00640  * @note
00641  * @param
00642  * @retval
00643  */
00644 uip_userdata_t* TcpClient::_allocateData()
00645 {
00646     for (uint8_t sock = 0; sock < UIP_CONNS; sock++) {
00647         uip_userdata_t*     data = &TcpClient::all_data[sock];
00648         if (!data->state) {
00649             data->pollTimer.reset();
00650             data->state = sock | UIP_CLIENT_CONNECTED;
00651             data->ripaddr[0] = 0;
00652             data->ripaddr[1] = 0;
00653             memset(data->packets_in, 0, sizeof(data->packets_in) / sizeof(data->packets_in[0]));
00654             memset(&data->packets_out, 0, sizeof(data->packets_out) / sizeof(data->packets_out[0]));
00655             data->out_pos = 0;
00656             return data;
00657         }
00658     }
00659 
00660     return NULL;
00661 }
00662 
00663 /**
00664  * @brief
00665  * @note
00666  * @param
00667  * @retval
00668  */
00669 uint8_t TcpClient::_currentBlock(memhandle* block)
00670 {
00671     for (uint8_t i = 1; i < UIP_SOCKET_NUMPACKETS; i++) {
00672         if (block[i] == NOBLOCK)
00673             return i - 1;
00674     }
00675 
00676     return UIP_SOCKET_NUMPACKETS - 1;
00677 }
00678 
00679 /**
00680  * @brief
00681  * @note
00682  * @param
00683  * @retval
00684  */
00685 void TcpClient::_eatBlock(memhandle* block)
00686 {
00687 #ifdef UIPETHERNET_DEBUG_CLIENT
00688     memhandle*  start = block;
00689     printf("eatblock(%d): ", *block);
00690     for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) {
00691         printf("%d ", start[i]);
00692     }
00693 
00694     printf("-> ");
00695 #endif
00696     UipEthernet::ethernet->enc28j60Eth.freeBlock(block[0]);
00697     for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS - 1; i++) {
00698         block[i] = block[i + 1];
00699     }
00700 
00701     block[UIP_SOCKET_NUMPACKETS - 1] = NOBLOCK;
00702 #ifdef UIPETHERNET_DEBUG_CLIENT
00703     for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) {
00704         printf("%d ", start[i]);
00705     }
00706 
00707     printf("\r\n");
00708 #endif
00709 }
00710 
00711 /**
00712  * @brief
00713  * @note
00714  * @param
00715  * @retval
00716  */
00717 void TcpClient::_flushBlocks(memhandle* block)
00718 {
00719     for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) {
00720         UipEthernet::ethernet->enc28j60Eth.freeBlock(block[i]);
00721         block[i] = NOBLOCK;
00722     }
00723 }
00724 
00725 #ifdef UIPETHERNET_DEBUG_CLIENT
00726 
00727 /**
00728  * @brief
00729  * @note
00730  * @param
00731  * @retval
00732  */
00733 void UIPClient::_dumpAllData()
00734 {
00735     for (uint8_t i = 0; i < UIP_CONNS; i++) {
00736         printf("UIPClient::all_data[%d], state:%d packets_in: ", i, all_data[i].state);
00737         for (uint8_t j = 0; j < UIP_SOCKET_NUMPACKETS; j++) {
00738             printf("%d ", all_data[i].packets_in[j]);
00739         }
00740 
00741         printf("\r\n");
00742         if (all_data[i].state & UIP_CLIENT_REMOTECLOSED) {
00743             printf("state remote closed, local port: %d", htons(((uip_userdata_closed_t *) (&all_data[i]))->lport));
00744             printf("\r\n");
00745         }
00746         else {
00747             printf("packets_out: ");
00748             for (uint8_t j = 0; j < UIP_SOCKET_NUMPACKETS; j++) {
00749                 printf("%d ", all_data[i].packets_out[j]);
00750             }
00751 
00752             printf("\r\n");
00753             printf("out_pos: %d\r\n", all_data[i].out_pos);
00754         }
00755     }
00756 }
00757 #endif