mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #8499 from michalpasztamobica/master
Socket closing improvements and tests adjustmentspull/8572/head
commit
9403a2f16c
|
@ -95,7 +95,8 @@ public:
|
|||
enum SocketType {
|
||||
TCP_CLIENT,
|
||||
TCP_SERVER,
|
||||
UDP
|
||||
UDP,
|
||||
OTHER
|
||||
};
|
||||
SInfo(TCPSocket *sock):
|
||||
_id(id_count++),
|
||||
|
@ -151,6 +152,24 @@ public:
|
|||
{
|
||||
assert(sock);
|
||||
}
|
||||
SInfo(Socket* sock, bool delete_on_exit=true):
|
||||
_id(id_count++),
|
||||
_sock(sock),
|
||||
_type(SInfo::OTHER),
|
||||
_blocking(true),
|
||||
_dataLen(0),
|
||||
_maxRecvLen(0),
|
||||
_repeatBufferFill(1),
|
||||
_receivedTotal(0),
|
||||
_receiverThread(NULL),
|
||||
_receiveBuffer(NULL),
|
||||
_senderThreadId(NULL),
|
||||
_receiverThreadId(NULL),
|
||||
_packetSizes(NULL),
|
||||
_check_pattern(false)
|
||||
{
|
||||
MBED_ASSERT(sock);
|
||||
}
|
||||
~SInfo()
|
||||
{
|
||||
this->_sock->sigio(Callback<void()>());
|
||||
|
@ -1109,40 +1128,48 @@ static int cmd_socket(int argc, char *argv[])
|
|||
}
|
||||
|
||||
/*
|
||||
* Commands for TCPServer
|
||||
* Commands for TCPServer and TCPSocket
|
||||
* listen, accept
|
||||
*/
|
||||
if ((COMMAND_IS("listen") || COMMAND_IS("accept")) && info->type() != SInfo::TCP_SERVER) {
|
||||
cmd_printf("Not TCPServer\r\n");
|
||||
return CMDLINE_RETCODE_FAIL;
|
||||
}
|
||||
if (COMMAND_IS("listen")) {
|
||||
int32_t backlog;
|
||||
if (cmd_parameter_int(argc, argv, "listen", &backlog)) {
|
||||
return handle_nsapi_error("TCPServer::listen()", static_cast<TCPServer &>(info->socket()).listen(backlog));
|
||||
return handle_nsapi_error("Socket::listen()", info->socket().listen(backlog));
|
||||
} else {
|
||||
return handle_nsapi_error("TCPServer::listen()", static_cast<TCPServer &>(info->socket()).listen());
|
||||
return handle_nsapi_error("Socket::listen()", info->socket().listen());
|
||||
}
|
||||
|
||||
} else if (COMMAND_IS("accept")) {
|
||||
SocketAddress addr;
|
||||
int32_t id;
|
||||
if (!cmd_parameter_int(argc, argv, "accept", &id)) {
|
||||
cmd_printf("Need new socket id\r\n");
|
||||
return CMDLINE_RETCODE_INVALID_PARAMETERS;
|
||||
nsapi_error_t ret;
|
||||
if (info->type() != SInfo::TCP_SERVER) {
|
||||
Socket *new_sock = info->socket().accept(&ret);
|
||||
if (ret == NSAPI_ERROR_OK) {
|
||||
SInfo *new_info = new SInfo(new_sock);
|
||||
m_sockets.push_back(new_info);
|
||||
cmd_printf("Socket::accept() new socket sid: %d\r\n", new_info->id());
|
||||
}
|
||||
return handle_nsapi_error("Socket::accept()", ret);
|
||||
} else { // Old TCPServer API
|
||||
int32_t id;
|
||||
SocketAddress addr;
|
||||
|
||||
if (!cmd_parameter_int(argc, argv, "accept", &id)) {
|
||||
cmd_printf("Need new socket id\r\n");
|
||||
return CMDLINE_RETCODE_INVALID_PARAMETERS;
|
||||
}
|
||||
SInfo *new_info = get_sinfo(id);
|
||||
if (!new_info) {
|
||||
cmd_printf("Invalid socket id\r\n");
|
||||
return CMDLINE_RETCODE_FAIL;
|
||||
}
|
||||
TCPSocket *new_sock = static_cast<TCPSocket*>(&new_info->socket());
|
||||
nsapi_error_t ret = static_cast<TCPServer&>(info->socket()).accept(new_sock, &addr);
|
||||
if (ret == NSAPI_ERROR_OK) {
|
||||
cmd_printf("TCPServer::accept() new socket sid: %d connection from %s port %d\r\n",
|
||||
new_info->id(), addr.get_ip_address(), addr.get_port());
|
||||
}
|
||||
return handle_nsapi_error("TCPServer::accept()", ret);
|
||||
}
|
||||
SInfo *new_info = get_sinfo(id);
|
||||
if (!new_info) {
|
||||
cmd_printf("Invalid socket id\r\n");
|
||||
return CMDLINE_RETCODE_FAIL;
|
||||
}
|
||||
TCPSocket *new_sock = static_cast<TCPSocket *>(&new_info->socket());
|
||||
nsapi_error_t ret = static_cast<TCPServer &>(info->socket()).accept(new_sock, &addr);
|
||||
if (ret == NSAPI_ERROR_OK) {
|
||||
cmd_printf("TCPServer::accept() new socket sid: %d connection from %s port %d\r\n",
|
||||
new_info->id(), addr.get_ip_address(), addr.get_port());
|
||||
}
|
||||
return handle_nsapi_error("TCPServer::accept()", ret);
|
||||
}
|
||||
return CMDLINE_RETCODE_INVALID_PARAMETERS;
|
||||
}
|
||||
|
|
|
@ -43,15 +43,15 @@ class MultipleTestcase(Bench):
|
|||
|
||||
def socket_bind_port(self, socket_type):
|
||||
response = self.command("dut1", "socket new " + socket_type)
|
||||
socket_id = int(response.parsed['socket_id'])
|
||||
self.socket_id = int(response.parsed['socket_id'])
|
||||
|
||||
self.command("dut1", "socket " + str(socket_id) + " open")
|
||||
self.command("dut1", "socket " + str(self.socket_id) + " open")
|
||||
|
||||
self.command("dut1", "socket " + str(socket_id) + " bind port 1024")
|
||||
|
||||
self.command("dut1", "socket " + str(socket_id) + " delete")
|
||||
self.command("dut1", "socket " + str(self.socket_id) + " bind port 1024")
|
||||
|
||||
def teardown(self):
|
||||
self.command("dut1", "socket " + str(self.socket_id) + " delete")
|
||||
|
||||
self.command("dut1", "ifdown")
|
||||
|
||||
|
||||
|
|
|
@ -62,15 +62,15 @@ class Testcase(Bench):
|
|||
self.used_port = 2000
|
||||
|
||||
response = self.command("dut1", "socket new TCPServer")
|
||||
server_base_socket_id = int(response.parsed['socket_id'])
|
||||
self.server_base_socket_id = int(response.parsed['socket_id'])
|
||||
|
||||
self.command("dut1", "socket " + str(server_base_socket_id) + " open")
|
||||
self.command("dut1", "socket " + str(server_base_socket_id) + " bind port " + str(self.used_port))
|
||||
self.command("dut1", "socket " + str(server_base_socket_id) + " listen")
|
||||
self.command("dut1", "socket " + str(self.server_base_socket_id) + " open")
|
||||
self.command("dut1", "socket " + str(self.server_base_socket_id) + " bind port " + str(self.used_port))
|
||||
self.command("dut1", "socket " + str(self.server_base_socket_id) + " listen")
|
||||
|
||||
response = self.command("dut1", "socket new TCPSocket")
|
||||
server_socket_id = int(response.parsed['socket_id'])
|
||||
self.command("dut1", "socket " + str(server_socket_id) + " open")
|
||||
self.server_socket_id = int(response.parsed['socket_id'])
|
||||
self.command("dut1", "socket " + str(self.server_socket_id) + " open")
|
||||
|
||||
response = self.command("dut2", "socket new TCPSocket")
|
||||
zero = response.timedelta
|
||||
|
@ -81,7 +81,7 @@ class Testcase(Bench):
|
|||
t.start()
|
||||
|
||||
wait = 5
|
||||
response = self.command("dut1", "socket " + str(server_base_socket_id) + " accept " + str(server_socket_id))
|
||||
response = self.command("dut1", "socket " + str(self.server_base_socket_id) + " accept " + str(self.server_socket_id))
|
||||
response.verify_response_duration(expected=wait, zero=zero, threshold_percent=10, break_in_fail=True)
|
||||
socket_id = int(response.parsed['socket_id'])
|
||||
|
||||
|
@ -94,10 +94,10 @@ class Testcase(Bench):
|
|||
if data != "hello":
|
||||
raise TestStepFail("Received data doesn't match the sent data")
|
||||
|
||||
self.command("dut1", "socket " + str(server_socket_id) + " delete")
|
||||
self.command("dut1", "socket " + str(server_base_socket_id) + " delete")
|
||||
self.command("dut2", "socket " + str(self.client_socket_id) + " delete")
|
||||
|
||||
def teardown(self):
|
||||
self.command("dut1", "socket " + str(self.server_socket_id) + " delete")
|
||||
self.command("dut1", "socket " + str(self.server_base_socket_id) + " delete")
|
||||
self.command("dut2", "socket " + str(self.client_socket_id) + " delete")
|
||||
|
||||
interfaceDown(self, ["dut1"])
|
||||
interfaceDown(self, ["dut2"])
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
"""
|
||||
Copyright 2018 ARM Limited
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import threading
|
||||
import time
|
||||
|
||||
from icetea_lib.bench import Bench
|
||||
from interface import interfaceUp, interfaceDown
|
||||
#from mbed_clitest.tools import test_case
|
||||
#from mbed_clitest.TestStepError import SkippedTestcaseException
|
||||
from icetea_lib.TestStepError import TestStepFail
|
||||
|
||||
class Testcase(Bench):
|
||||
def __init__(self):
|
||||
Bench.__init__(self,
|
||||
name="TCPSOCKET_ACCEPT",
|
||||
title = "TCPSOCKET_ACCEPT",
|
||||
purpose = "Test that TCPSocket::bind(), TCPSocket::listen() and TCPSocket::accept() works",
|
||||
status = "released",
|
||||
component= ["mbed-os", "netsocket"],
|
||||
type="smoke",
|
||||
subtype="socket",
|
||||
requirements={
|
||||
"duts": {
|
||||
'*': { #requirements for all nodes
|
||||
"count":2,
|
||||
"type": "hardware",
|
||||
"application": {
|
||||
"name": "TEST_APPS-device-socket_app"
|
||||
}
|
||||
},
|
||||
"1": {"nick": "dut1"},
|
||||
"2": {"nick": "dut2"}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def setup(self):
|
||||
interface = interfaceUp(self, ["dut1"])
|
||||
self.server_ip = interface["dut1"]["ip"]
|
||||
interface = interfaceUp(self, ["dut2"])
|
||||
self.client_ip = interface["dut2"]["ip"]
|
||||
|
||||
def clientThread(self):
|
||||
self.logger.info("Starting")
|
||||
time.sleep(5) #wait accept from server
|
||||
self.command("dut2", "socket " + str(self.client_socket_id) + " open")
|
||||
self.command("dut2", "socket " + str(self.client_socket_id) + " connect " + str(self.server_ip) + " " + str(self.used_port))
|
||||
|
||||
|
||||
def case(self):
|
||||
self.used_port = 2000
|
||||
|
||||
response = self.command("dut1", "socket new TCPSocket")
|
||||
self.server_base_socket_id = int(response.parsed['socket_id'])
|
||||
|
||||
self.command("dut1", "socket " + str(self.server_base_socket_id) + " open")
|
||||
response = self.command("dut1", "socket " + str(self.server_base_socket_id) + " bind port " + str(self.used_port), report_cmd_fail = False)
|
||||
if response.retcode == -1:
|
||||
if (response.verify_trace("NSAPI_ERROR_UNSUPPORTED", break_in_fail = False)):
|
||||
raise SkippedTestcaseException("UNSUPPORTED")
|
||||
else:
|
||||
TestStepFail("Bind port failed")
|
||||
self.command("dut1", "socket " + str(self.server_base_socket_id) + " listen")
|
||||
|
||||
response = self.command("dut2", "socket new TCPSocket")
|
||||
self.client_socket_id = int(response.parsed['socket_id'])
|
||||
|
||||
#Create a thread which calls client connect()
|
||||
t = threading.Thread(name='clientThread', target=self.clientThread)
|
||||
t.start()
|
||||
|
||||
response = self.command("dut1", "socket " + str(self.server_base_socket_id) + " accept")
|
||||
|
||||
t.join()
|
||||
self.accept_socket_id = int(response.parsed['socket_id'])
|
||||
if response.timedelta < 5.0: # Check that socket accept call blocks
|
||||
raise TestStepFail("Longer response time expected")
|
||||
|
||||
self.command("dut1", "socket " + str(self.accept_socket_id) + " send hello")
|
||||
|
||||
response = self.command("dut2", "socket " + str(self.client_socket_id) + " recv 5")
|
||||
data = response.parsed['data'].replace(":","")
|
||||
|
||||
if data != "hello":
|
||||
raise TestStepFail("Received data doesn't match the sent data")
|
||||
|
||||
def teardown(self):
|
||||
response = self.command("dut2", "socket " + str(self.client_socket_id) + " delete")
|
||||
response = self.command("dut1", "socket " + str(self.server_base_socket_id) + " delete")
|
||||
response = self.command("dut1", "socket " + str(self.accept_socket_id) + " close")
|
||||
|
||||
interfaceDown(self, ["dut1"])
|
||||
interfaceDown(self, ["dut2"])
|
|
@ -48,31 +48,30 @@ class Testcase(Bench):
|
|||
|
||||
def case(self):
|
||||
response = self.command("dut1", "socket new TCPSocket")
|
||||
socket_id = int(response.parsed['socket_id'])
|
||||
self.socket_id = int(response.parsed['socket_id'])
|
||||
|
||||
self.command("dut1", "socket " + str(socket_id) + " open")
|
||||
self.command("dut1", "socket " + str(socket_id) + " connect echo.mbedcloudtesting.com 7")
|
||||
self.command("dut1", "socket " + str(self.socket_id) + " open")
|
||||
self.command("dut1", "socket " + str(self.socket_id) + " connect echo.mbedcloudtesting.com 7")
|
||||
|
||||
for i in range(2):
|
||||
sentData = ""
|
||||
for size in (100, 200, 300, 120, 500):
|
||||
packet = Randomize.random_string(max_len=size, min_len=size, chars=string.ascii_uppercase)
|
||||
sentData += packet
|
||||
response = self.command("dut1", "socket " + str(socket_id) + " send " + str(packet))
|
||||
response = self.command("dut1", "socket " + str(self.socket_id) + " send " + str(packet))
|
||||
response.verify_trace("TCPSocket::send() returned: " + str(size))
|
||||
|
||||
received = 0
|
||||
data = ""
|
||||
totalSize = 1220
|
||||
while received < totalSize:
|
||||
response = self.command("dut1", "socket " + str(socket_id) + " recv " + str(totalSize))
|
||||
response = self.command("dut1", "socket " + str(self.socket_id) + " recv " + str(totalSize))
|
||||
data += response.parsed['data'].replace(":", "")
|
||||
received += int(response.parsed['received_bytes'])
|
||||
|
||||
if data != sentData:
|
||||
raise TestStepFail("Received data doesn't match the sent data")
|
||||
|
||||
self.command("dut1", "socket " + str(socket_id) + " delete")
|
||||
|
||||
def teardown(self):
|
||||
self.command("dut1", "socket " + str(self.socket_id) + " delete")
|
||||
self.command("dut1", "ifdown")
|
||||
|
|
|
@ -151,7 +151,7 @@ TEST_F(TestInternetSocket, close)
|
|||
TEST_F(TestInternetSocket, close_no_open)
|
||||
{
|
||||
stack.return_value = NSAPI_ERROR_OK;
|
||||
EXPECT_EQ(socket->close(), NSAPI_ERROR_OK);
|
||||
EXPECT_EQ(socket->close(), NSAPI_ERROR_NO_SOCKET);
|
||||
}
|
||||
|
||||
TEST_F(TestInternetSocket, close_during_read)
|
||||
|
|
|
@ -26,6 +26,11 @@ InternetSocket::InternetSocket()
|
|||
{
|
||||
}
|
||||
|
||||
InternetSocket::~InternetSocket()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
nsapi_error_t InternetSocket::open(NetworkStack *stack)
|
||||
{
|
||||
_lock.lock();
|
||||
|
@ -56,17 +61,16 @@ nsapi_error_t InternetSocket::close()
|
|||
_lock.lock();
|
||||
|
||||
nsapi_error_t ret = NSAPI_ERROR_OK;
|
||||
if (!_stack) {
|
||||
return ret;
|
||||
if (!_socket) {
|
||||
return NSAPI_ERROR_NO_SOCKET;
|
||||
}
|
||||
|
||||
if (_socket) {
|
||||
_stack->socket_attach(_socket, 0, 0);
|
||||
nsapi_socket_t socket = _socket;
|
||||
_socket = 0;
|
||||
ret = _stack->socket_close(socket);
|
||||
}
|
||||
_stack = 0;
|
||||
// Just in case - tell the stack not to callback any more, then remove this socket.
|
||||
_stack->socket_attach(_socket, 0, 0);
|
||||
nsapi_socket_t socket = _socket;
|
||||
_socket = 0;
|
||||
ret = _stack->socket_close(socket);
|
||||
_stack = 0; // Invalidate the stack pointer - otherwise open() fails.
|
||||
|
||||
// Wakeup anything in a blocking operation
|
||||
// on this socket
|
||||
|
@ -81,7 +85,7 @@ nsapi_error_t InternetSocket::close()
|
|||
|
||||
_lock.unlock();
|
||||
|
||||
// When allocated by accept() call, will self desctruct on close();
|
||||
// When allocated by accept() call, will destroy itself on close();
|
||||
if (_factory_allocated) {
|
||||
delete this;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
*
|
||||
* @note Closes socket if it's still open.
|
||||
*/
|
||||
virtual ~InternetSocket() {}
|
||||
virtual ~InternetSocket();
|
||||
|
||||
/** Open a network socket on the network stack of the given
|
||||
* network interface.
|
||||
|
|
|
@ -24,7 +24,6 @@ TCPServer::TCPServer()
|
|||
|
||||
TCPServer::~TCPServer()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
nsapi_error_t TCPServer::accept(TCPSocket *connection, SocketAddress *address)
|
||||
|
|
|
@ -22,9 +22,19 @@ TCPSocket::TCPSocket()
|
|||
{
|
||||
}
|
||||
|
||||
TCPSocket::TCPSocket(TCPSocket* parent, nsapi_socket_t socket, SocketAddress address)
|
||||
{
|
||||
_socket = socket,
|
||||
_stack = parent->_stack;
|
||||
_factory_allocated = true;
|
||||
_remote_peer = address;
|
||||
|
||||
_event = mbed::Callback<void()>(this, &TCPSocket::event);
|
||||
_stack->socket_attach(socket, &mbed::Callback<void()>::thunk, &_event);
|
||||
}
|
||||
|
||||
TCPSocket::~TCPSocket()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
nsapi_protocol_t TCPSocket::get_proto()
|
||||
|
@ -265,16 +275,7 @@ TCPSocket *TCPSocket::accept(nsapi_error_t *error)
|
|||
ret = _stack->socket_accept(_socket, &socket, &address);
|
||||
|
||||
if (0 == ret) {
|
||||
connection = new TCPSocket();
|
||||
connection->_lock.lock();
|
||||
connection->_factory_allocated = true; // Destroy automatically on close()
|
||||
connection->_remote_peer = address;
|
||||
connection->_stack = _stack;
|
||||
connection->_socket = socket;
|
||||
connection->_event = mbed::Callback<void()>(connection, &TCPSocket::event);
|
||||
_stack->socket_attach(socket, &mbed::Callback<void()>::thunk, &connection->_event);
|
||||
|
||||
connection->_lock.unlock();
|
||||
connection = new TCPSocket(this, socket, address);
|
||||
break;
|
||||
} else if ((_timeout == 0) || (ret != NSAPI_ERROR_WOULD_BLOCK)) {
|
||||
break;
|
||||
|
|
|
@ -182,6 +182,13 @@ public:
|
|||
protected:
|
||||
friend class TCPServer;
|
||||
virtual nsapi_protocol_t get_proto();
|
||||
|
||||
private:
|
||||
/** Create a socket out of a given socket
|
||||
*
|
||||
* To be used within accept() function. Close() will clean this up.
|
||||
*/
|
||||
TCPSocket(TCPSocket *parent, nsapi_socket_t socket, SocketAddress address);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ UDPSocket::UDPSocket()
|
|||
|
||||
UDPSocket::~UDPSocket()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
nsapi_protocol_t UDPSocket::get_proto()
|
||||
|
|
Loading…
Reference in New Issue