Nicolas Borla / Mbed OS BBR_1Ebene
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers TCPSocket.cpp Source File

TCPSocket.cpp

00001 /* Socket
00002  * Copyright (c) 2015 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include "TCPSocket.h"
00018 #include "Timer.h"
00019 #include "mbed_assert.h"
00020 
00021 #define READ_FLAG           0x1u
00022 #define WRITE_FLAG          0x2u
00023 
00024 TCPSocket::TCPSocket()
00025     : _pending(0), _event_flag(),
00026       _read_in_progress(false), _write_in_progress(false)
00027 {
00028 }
00029 
00030 TCPSocket::~TCPSocket()
00031 {
00032     close();
00033 }
00034 
00035 nsapi_protocol_t TCPSocket::get_proto()
00036 {
00037     return NSAPI_TCP ;
00038 }
00039 
00040 nsapi_error_t TCPSocket::connect(const SocketAddress &address)
00041 {
00042     _lock.lock();
00043     nsapi_error_t ret;
00044 
00045     // If this assert is hit then there are two threads
00046     // performing a send at the same time which is undefined
00047     // behavior
00048     MBED_ASSERT(!_write_in_progress);
00049     _write_in_progress = true;
00050 
00051     bool blocking_connect_in_progress = false;
00052 
00053     while (true) {
00054         if (!_socket) {
00055             ret = NSAPI_ERROR_NO_SOCKET ;
00056             break;
00057         }
00058 
00059         _pending = 0;
00060         ret = _stack->socket_connect(_socket, address);
00061         if ((_timeout == 0) || !(ret == NSAPI_ERROR_IN_PROGRESS  || ret == NSAPI_ERROR_ALREADY )) {
00062             break;
00063         } else {
00064             blocking_connect_in_progress = true;
00065 
00066             uint32_t flag;
00067 
00068             // Release lock before blocking so other threads
00069             // accessing this object aren't blocked
00070             _lock.unlock();
00071             flag = _event_flag.wait_any(WRITE_FLAG, _timeout);
00072             _lock.lock();
00073             if (flag & osFlagsError) {
00074                 // Timeout break
00075                 break;
00076             }
00077         }
00078     }
00079 
00080     _write_in_progress = false;
00081 
00082     /* Non-blocking connect gives "EISCONN" once done - convert to OK for blocking mode if we became connected during this call */
00083     if (ret == NSAPI_ERROR_IS_CONNECTED  && blocking_connect_in_progress) {
00084         ret = NSAPI_ERROR_OK ;
00085     }
00086 
00087     _lock.unlock();
00088     return ret;
00089 }
00090 
00091 nsapi_error_t TCPSocket::connect(const char *host, uint16_t port)
00092 {
00093     SocketAddress address;
00094     nsapi_error_t err = _stack->gethostbyname(host, &address);
00095     if (err) {
00096         return NSAPI_ERROR_DNS_FAILURE ;
00097     }
00098 
00099     address.set_port(port);
00100 
00101     // connect is thread safe
00102     return connect(address);
00103 }
00104 
00105 nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size)
00106 {
00107     _lock.lock();
00108     const uint8_t *data_ptr = static_cast<const uint8_t *>(data);
00109     nsapi_size_or_error_t ret;
00110     nsapi_size_t written = 0;
00111 
00112     // If this assert is hit then there are two threads
00113     // performing a send at the same time which is undefined
00114     // behavior
00115     MBED_ASSERT(!_write_in_progress);
00116     _write_in_progress = true;
00117 
00118     // Unlike recv, we should write the whole thing if blocking. POSIX only
00119     // allows partial as a side-effect of signal handling; it normally tries to
00120     // write everything if blocking. Without signals we can always write all.
00121     while (true) {
00122         if (!_socket) {
00123             ret = NSAPI_ERROR_NO_SOCKET ;
00124             break;
00125         }
00126 
00127         _pending = 0;
00128         ret = _stack->socket_send(_socket, data_ptr + written, size - written);
00129         if (ret >= 0) {
00130             written += ret;
00131             if (written >= size) {
00132                 break;
00133             }
00134         }
00135         if (_timeout == 0) {
00136             break;
00137         } else if (ret == NSAPI_ERROR_WOULD_BLOCK ) {
00138             uint32_t flag;
00139 
00140             // Release lock before blocking so other threads
00141             // accessing this object aren't blocked
00142             _lock.unlock();
00143             flag = _event_flag.wait_any(WRITE_FLAG, _timeout);
00144             _lock.lock();
00145 
00146             if (flag & osFlagsError) {
00147                 // Timeout break
00148                 break;
00149             }
00150         } else if (ret < 0) {
00151             break;
00152         }
00153     }
00154 
00155     _write_in_progress = false;
00156     _lock.unlock();
00157     if (ret <= 0 && ret != NSAPI_ERROR_WOULD_BLOCK ) {
00158         return ret;
00159     } else if (written == 0) {
00160         return NSAPI_ERROR_WOULD_BLOCK ;
00161     } else {
00162         return written;
00163     }
00164 }
00165 
00166 nsapi_size_or_error_t TCPSocket::recv(void *data, nsapi_size_t size)
00167 {
00168     _lock.lock();
00169     nsapi_size_or_error_t ret;
00170 
00171     // If this assert is hit then there are two threads
00172     // performing a recv at the same time which is undefined
00173     // behavior
00174     MBED_ASSERT(!_read_in_progress);
00175     _read_in_progress = true;
00176 
00177     while (true) {
00178         if (!_socket) {
00179             ret = NSAPI_ERROR_NO_SOCKET ;
00180             break;
00181         }
00182 
00183         _pending = 0;
00184         ret = _stack->socket_recv(_socket, data, size);
00185         if ((_timeout == 0) || (ret != NSAPI_ERROR_WOULD_BLOCK )) {
00186             break;
00187         } else {
00188             uint32_t flag;
00189 
00190             // Release lock before blocking so other threads
00191             // accessing this object aren't blocked
00192             _lock.unlock();
00193             flag = _event_flag.wait_any(READ_FLAG, _timeout);
00194             _lock.lock();
00195 
00196             if (flag & osFlagsError) {
00197                 // Timeout break
00198                 ret = NSAPI_ERROR_WOULD_BLOCK ;
00199                 break;
00200             }
00201         }
00202     }
00203 
00204     _read_in_progress = false;
00205     _lock.unlock();
00206     return ret;
00207 }
00208 
00209 void TCPSocket::event()
00210 {
00211     _event_flag.set(READ_FLAG|WRITE_FLAG);
00212 
00213     _pending += 1;
00214     if (_callback && _pending == 1) {
00215         _callback();
00216     }
00217 }