takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

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 TCPSocket::TCPSocket()
00022 {
00023 }
00024 
00025 TCPSocket::~TCPSocket()
00026 {
00027     close();
00028 }
00029 
00030 nsapi_protocol_t TCPSocket::get_proto()
00031 {
00032     return NSAPI_TCP ;
00033 }
00034 
00035 nsapi_error_t TCPSocket::connect(const SocketAddress &address)
00036 {
00037     _lock.lock();
00038     nsapi_error_t ret;
00039 
00040     // If this assert is hit then there are two threads
00041     // performing a send at the same time which is undefined
00042     // behavior
00043     MBED_ASSERT(_writers == 0);
00044     _writers++;
00045 
00046     bool blocking_connect_in_progress = false;
00047 
00048     while (true) {
00049         if (!_socket) {
00050             ret = NSAPI_ERROR_NO_SOCKET ;
00051             break;
00052         }
00053 
00054         _pending = 0;
00055         ret = _stack->socket_connect(_socket, address);
00056         if ((_timeout == 0) || !(ret == NSAPI_ERROR_IN_PROGRESS  || ret == NSAPI_ERROR_ALREADY )) {
00057             break;
00058         } else {
00059             blocking_connect_in_progress = true;
00060 
00061             uint32_t flag;
00062 
00063             // Release lock before blocking so other threads
00064             // accessing this object aren't blocked
00065             _lock.unlock();
00066             flag = _event_flag.wait_any(WRITE_FLAG, _timeout);
00067             _lock.lock();
00068             if (flag & osFlagsError) {
00069                 // Timeout break
00070                 break;
00071             }
00072         }
00073     }
00074 
00075     _writers--;
00076     if (!_socket) {
00077         _event_flag.set(FINISHED_FLAG);
00078     }
00079 
00080     /* Non-blocking connect gives "EISCONN" once done - convert to OK for blocking mode if we became connected during this call */
00081     if (ret == NSAPI_ERROR_IS_CONNECTED  && blocking_connect_in_progress) {
00082         ret = NSAPI_ERROR_OK ;
00083     }
00084 
00085     if (ret == NSAPI_ERROR_OK  || ret == NSAPI_ERROR_IN_PROGRESS ) {
00086         _remote_peer = address;
00087     }
00088 
00089     _lock.unlock();
00090     return ret;
00091 }
00092 
00093 nsapi_error_t TCPSocket::connect(const char *host, uint16_t port)
00094 {
00095     SocketAddress address;
00096     nsapi_error_t err = _stack->gethostbyname(host, &address);
00097     if (err) {
00098         return NSAPI_ERROR_DNS_FAILURE ;
00099     }
00100 
00101     address.set_port(port);
00102 
00103     // connect is thread safe
00104     return connect(address);
00105 }
00106 
00107 nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size)
00108 {
00109     _lock.lock();
00110     const uint8_t *data_ptr = static_cast<const uint8_t *>(data);
00111     nsapi_size_or_error_t ret;
00112     nsapi_size_t written = 0;
00113 
00114     // If this assert is hit then there are two threads
00115     // performing a send at the same time which is undefined
00116     // behavior
00117     MBED_ASSERT(_writers == 0);
00118     _writers++;
00119 
00120     // Unlike recv, we should write the whole thing if blocking. POSIX only
00121     // allows partial as a side-effect of signal handling; it normally tries to
00122     // write everything if blocking. Without signals we can always write all.
00123     while (true) {
00124         if (!_socket) {
00125             ret = NSAPI_ERROR_NO_SOCKET ;
00126             break;
00127         }
00128 
00129         _pending = 0;
00130         ret = _stack->socket_send(_socket, data_ptr + written, size - written);
00131         if (ret >= 0) {
00132             written += ret;
00133             if (written >= size) {
00134                 break;
00135             }
00136         }
00137         if (_timeout == 0) {
00138             break;
00139         } else if (ret == NSAPI_ERROR_WOULD_BLOCK ) {
00140             uint32_t flag;
00141 
00142             // Release lock before blocking so other threads
00143             // accessing this object aren't blocked
00144             _lock.unlock();
00145             flag = _event_flag.wait_any(WRITE_FLAG, _timeout);
00146             _lock.lock();
00147 
00148             if (flag & osFlagsError) {
00149                 // Timeout break
00150                 break;
00151             }
00152         } else if (ret < 0) {
00153             break;
00154         }
00155     }
00156 
00157     _writers--;
00158     if (!_socket) {
00159         _event_flag.set(FINISHED_FLAG);
00160     }
00161 
00162     _lock.unlock();
00163     if (ret <= 0 && ret != NSAPI_ERROR_WOULD_BLOCK ) {
00164         return ret;
00165     } else if (written == 0) {
00166         return NSAPI_ERROR_WOULD_BLOCK ;
00167     } else {
00168         return written;
00169     }
00170 }
00171 
00172 nsapi_size_or_error_t TCPSocket::sendto(const SocketAddress &address, const void *data, nsapi_size_t size)
00173 {
00174     (void)address;
00175     return send(data, size);
00176 }
00177 
00178 nsapi_size_or_error_t TCPSocket::recv(void *data, nsapi_size_t size)
00179 {
00180     _lock.lock();
00181     nsapi_size_or_error_t ret;
00182 
00183     // If this assert is hit then there are two threads
00184     // performing a recv at the same time which is undefined
00185     // behavior
00186     MBED_ASSERT(_readers == 0);
00187     _readers++;
00188 
00189     while (true) {
00190         if (!_socket) {
00191             ret = NSAPI_ERROR_NO_SOCKET ;
00192             break;
00193         }
00194 
00195         _pending = 0;
00196         ret = _stack->socket_recv(_socket, data, size);
00197         if ((_timeout == 0) || (ret != NSAPI_ERROR_WOULD_BLOCK )) {
00198             break;
00199         } else {
00200             uint32_t flag;
00201 
00202             // Release lock before blocking so other threads
00203             // accessing this object aren't blocked
00204             _lock.unlock();
00205             flag = _event_flag.wait_any(READ_FLAG, _timeout);
00206             _lock.lock();
00207 
00208             if (flag & osFlagsError) {
00209                 // Timeout break
00210                 ret = NSAPI_ERROR_WOULD_BLOCK ;
00211                 break;
00212             }
00213         }
00214     }
00215 
00216     _readers--;
00217     if (!_socket) {
00218         _event_flag.set(FINISHED_FLAG);
00219     }
00220 
00221     _lock.unlock();
00222     return ret;
00223 }
00224 
00225 nsapi_size_or_error_t TCPSocket::recvfrom(SocketAddress *address, void *data, nsapi_size_t size)
00226 {
00227     if (address) {
00228         *address = _remote_peer;
00229     }
00230     return recv(data, size);
00231 }
00232 
00233 nsapi_error_t TCPSocket::listen(int backlog)
00234 {
00235     _lock.lock();
00236     nsapi_error_t ret;
00237 
00238     if (!_socket) {
00239         ret = NSAPI_ERROR_NO_SOCKET ;
00240     } else {
00241         ret = _stack->socket_listen(_socket, backlog);
00242     }
00243 
00244     _lock.unlock();
00245     return ret;
00246 }
00247 
00248 TCPSocket *TCPSocket::accept(nsapi_error_t *error)
00249 {
00250     _lock.lock();
00251     TCPSocket *connection = NULL;
00252     nsapi_error_t ret;
00253 
00254     _readers++;
00255 
00256     while (true) {
00257         if (!_socket) {
00258             ret = NSAPI_ERROR_NO_SOCKET ;
00259             break;
00260         }
00261 
00262         _pending = 0;
00263         void *socket;
00264         SocketAddress address;
00265         ret = _stack->socket_accept(_socket, &socket, &address);
00266 
00267         if (0 == ret) {
00268             connection = new TCPSocket();
00269             connection->_lock.lock();
00270             connection->_factory_allocated = true; // Destroy automatically on close()
00271             connection->_remote_peer = address;
00272             connection->_stack = _stack;
00273             connection->_socket = socket;
00274             connection->_event = mbed::Callback<void()>(connection, &TCPSocket::event);
00275             _stack->socket_attach(socket, &mbed::Callback<void()>::thunk, &connection->_event);
00276 
00277             connection->_lock.unlock();
00278             break;
00279         } else if ((_timeout == 0) || (ret != NSAPI_ERROR_WOULD_BLOCK )) {
00280             break;
00281         } else {
00282             uint32_t flag;
00283 
00284             // Release lock before blocking so other threads
00285             // accessing this object aren't blocked
00286             _lock.unlock();
00287             flag = _event_flag.wait_any(READ_FLAG, _timeout);
00288             _lock.lock();
00289 
00290             if (flag & osFlagsError) {
00291                 // Timeout break
00292                 ret = NSAPI_ERROR_WOULD_BLOCK ;
00293                 break;
00294             }
00295         }
00296     }
00297 
00298     _readers--;
00299     if (!_socket) {
00300         _event_flag.set(FINISHED_FLAG);
00301     }
00302     _lock.unlock();
00303     if (error) {
00304         *error = ret;
00305     }
00306     return connection;
00307 }