Skip to content

Commit 8c3c98b

Browse files
authored
Merge pull request #2332 from bridadan/add-net-test
Patch of #2318
2 parents e4db590 + c355fb1 commit 8c3c98b

File tree

7 files changed

+622
-0
lines changed

7 files changed

+622
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
host_tests/*
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Copyright 2015 ARM Limited, All rights reserved
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import sys
16+
import select
17+
import socket
18+
import logging
19+
from threading import Thread
20+
from sys import stdout
21+
from SocketServer import BaseRequestHandler, TCPServer
22+
from mbed_host_tests import BaseHostTest, event_callback
23+
24+
25+
class TCPEchoClientHandler(BaseRequestHandler):
26+
def handle(self):
27+
"""
28+
Handles a connection. Test starts by client(i.e. mbed) connecting to server.
29+
This connection handler receives data and echoes back to the client util
30+
{{end}} is received. Then it sits on recv() for client to terminate the
31+
connection.
32+
33+
Note: reason for not echoing data back after receiving {{end}} is that send
34+
fails raising a SocketError as client closes connection.
35+
"""
36+
print ("HOST: TCPEchoClient_Handler: Connection received...")
37+
while self.server.isrunning():
38+
try:
39+
data = self.recv()
40+
if not data: break
41+
except Exception as e:
42+
print ('HOST: TCPEchoClient_Handler recv error: %s' % str(e))
43+
break
44+
45+
print ('HOST: TCPEchoClient_Handler: Rx: \n%s\n' % data)
46+
47+
try:
48+
# echo data back to the client
49+
self.send(data)
50+
except Exception as e:
51+
print ('HOST: TCPEchoClient_Handler send error: %s' % str(e))
52+
break
53+
print 'Connection finished'
54+
55+
def recv(self):
56+
"""
57+
Try to receive until server is shutdown
58+
"""
59+
while self.server.isrunning():
60+
rl, wl, xl = select.select([self.request], [], [], 1)
61+
if len(rl):
62+
return self.request.recv(1024)
63+
64+
def send(self, data):
65+
"""
66+
Try to send until server is shutdown
67+
"""
68+
while self.server.isrunning():
69+
rl, wl, xl = select.select([], [self.request], [], 1)
70+
if len(wl):
71+
self.request.sendall(data)
72+
break
73+
74+
75+
class TCPServerWrapper(TCPServer):
76+
"""
77+
Wrapper over TCP server to implement server initiated shutdown.
78+
Adds a flag:= running that a request handler can check and come out of
79+
recv loop when shutdown is called.
80+
"""
81+
82+
def __init__(self, addr, request_handler):
83+
# hmm, TCPServer is not sub-classed from object!
84+
if issubclass(TCPServer, object):
85+
super(TCPServerWrapper, self).__init__(addr, request_handler)
86+
else:
87+
TCPServer.__init__(self, addr, request_handler)
88+
self.running = False
89+
90+
def serve_forever(self):
91+
self.running = True
92+
if issubclass(TCPServer, object):
93+
super(TCPServerWrapper, self).serve_forever()
94+
else:
95+
TCPServer.serve_forever(self)
96+
97+
def shutdown(self):
98+
self.running = False
99+
if issubclass(TCPServer, object):
100+
super(TCPServerWrapper, self).shutdown()
101+
else:
102+
TCPServer.shutdown(self)
103+
104+
def isrunning(self):
105+
return self.running
106+
107+
108+
class TCPEchoClientTest(BaseHostTest):
109+
110+
def __init__(self):
111+
"""
112+
Initialise test parameters.
113+
114+
:return:
115+
"""
116+
BaseHostTest.__init__(self)
117+
self.SERVER_IP = None # Will be determined after knowing the target IP
118+
self.SERVER_PORT = 0 # Let TCPServer choose an arbitrary port
119+
self.server = None
120+
self.server_thread = None
121+
self.target_ip = None
122+
123+
@staticmethod
124+
def find_interface_to_target_addr(target_ip):
125+
"""
126+
Finds IP address of the interface through which it is connected to the target.
127+
128+
:return:
129+
"""
130+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
131+
s.connect((target_ip, 0)) # Target IP, Any port
132+
ip = s.getsockname()[0]
133+
s.close()
134+
return ip
135+
136+
def setup_tcp_server(self):
137+
"""
138+
sets up a TCP server for target to connect and send test data.
139+
140+
:return:
141+
"""
142+
# !NOTE: There should mechanism to assert in the host test
143+
if self.SERVER_IP is None:
144+
self.log("setup_tcp_server() called before determining server IP!")
145+
self.notify_complete(False)
146+
147+
# Returning none will suppress host test from printing success code
148+
self.server = TCPServerWrapper((self.SERVER_IP, self.SERVER_PORT), TCPEchoClientHandler)
149+
ip, port = self.server.server_address
150+
self.SERVER_PORT = port
151+
self.server.allow_reuse_address = True
152+
self.log("HOST: Listening for TCP connections: " + self.SERVER_IP + ":" + str(self.SERVER_PORT))
153+
self.server_thread = Thread(target=TCPEchoClientTest.server_thread_func, args=(self,))
154+
self.server_thread.start()
155+
156+
@staticmethod
157+
def server_thread_func(this):
158+
"""
159+
Thread function to run TCP server forever.
160+
161+
:param this:
162+
:return:
163+
"""
164+
this.server.serve_forever()
165+
166+
@event_callback("target_ip")
167+
def _callback_target_ip(self, key, value, timestamp):
168+
"""
169+
Callback to handle reception of target's IP address.
170+
171+
:param key:
172+
:param value:
173+
:param timestamp:
174+
:return:
175+
"""
176+
self.target_ip = value
177+
self.SERVER_IP = self.find_interface_to_target_addr(self.target_ip)
178+
self.setup_tcp_server()
179+
180+
@event_callback("host_ip")
181+
def _callback_host_ip(self, key, value, timestamp):
182+
"""
183+
Callback for request for host IP Addr
184+
185+
"""
186+
self.send_kv("host_ip", self.SERVER_IP)
187+
188+
@event_callback("host_port")
189+
def _callback_host_port(self, key, value, timestamp):
190+
"""
191+
Callback for request for host port
192+
"""
193+
self.send_kv("host_port", self.SERVER_PORT)
194+
195+
def teardown(self):
196+
if self.server:
197+
self.server.shutdown()
198+
self.server_thread.join()
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
"""
2+
mbed SDK
3+
Copyright (c) 2011-2013 ARM Limited
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
"""
17+
18+
import sys
19+
import socket
20+
from sys import stdout
21+
from threading import Thread
22+
from SocketServer import BaseRequestHandler, UDPServer
23+
from mbed_host_tests import BaseHostTest, event_callback
24+
25+
26+
class UDPEchoClientHandler(BaseRequestHandler):
27+
def handle(self):
28+
""" UDP packet handler. Echoes data back to sender's address.
29+
"""
30+
data, sock = self.request
31+
print ('HOST: UDPEchoClientHandler: Rx: \n%s\n' % data)
32+
sock.sendto(data, self.client_address)
33+
34+
35+
class UDPEchoClientTest(BaseHostTest):
36+
37+
def __init__(self):
38+
"""
39+
Initialise test parameters.
40+
41+
:return:
42+
"""
43+
BaseHostTest.__init__(self)
44+
self.SERVER_IP = None # Will be determined after knowing the target IP
45+
self.SERVER_PORT = 0 # Let TCPServer choose an arbitrary port
46+
self.server = None
47+
self.server_thread = None
48+
self.target_ip = None
49+
50+
@staticmethod
51+
def find_interface_to_target_addr(target_ip):
52+
"""
53+
Finds IP address of the interface through which it is connected to the target.
54+
55+
:return:
56+
"""
57+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
58+
s.connect((target_ip, 0)) # Target IP, Any port
59+
ip = s.getsockname()[0]
60+
s.close()
61+
return ip
62+
63+
def setup_udp_server(self):
64+
"""
65+
sets up a UDP server for target to connect and send test data.
66+
67+
:return:
68+
"""
69+
# !NOTE: There should mechanism to assert in the host test
70+
if self.SERVER_IP is None:
71+
self.log("setup_udp_server() called before determining server IP!")
72+
self.notify_complete(False)
73+
74+
# Returning none will suppress host test from printing success code
75+
self.server = UDPServer((self.SERVER_IP, self.SERVER_PORT), UDPEchoClientHandler)
76+
ip, port = self.server.server_address
77+
self.SERVER_PORT = port
78+
self.server.allow_reuse_address = True
79+
self.log("HOST: Listening for UDP packets: " + self.SERVER_IP + ":" + str(self.SERVER_PORT))
80+
self.server_thread = Thread(target=UDPEchoClientTest.server_thread_func, args=(self,))
81+
self.server_thread.start()
82+
83+
@staticmethod
84+
def server_thread_func(this):
85+
"""
86+
Thread function to run TCP server forever.
87+
88+
:param this:
89+
:return:
90+
"""
91+
this.server.serve_forever()
92+
93+
@event_callback("target_ip")
94+
def _callback_target_ip(self, key, value, timestamp):
95+
"""
96+
Callback to handle reception of target's IP address.
97+
98+
:param key:
99+
:param value:
100+
:param timestamp:
101+
:return:
102+
"""
103+
self.target_ip = value
104+
self.SERVER_IP = self.find_interface_to_target_addr(self.target_ip)
105+
self.setup_udp_server()
106+
107+
@event_callback("host_ip")
108+
def _callback_host_ip(self, key, value, timestamp):
109+
"""
110+
Callback for request for host IP Addr
111+
112+
"""
113+
self.send_kv("host_ip", self.SERVER_IP)
114+
115+
@event_callback("host_port")
116+
def _callback_host_port(self, key, value, timestamp):
117+
"""
118+
Callback for request for host port
119+
"""
120+
self.send_kv("host_port", self.SERVER_PORT)
121+
122+
def teardown(self):
123+
if self.server:
124+
self.server.shutdown()
125+
self.server_thread.join()
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#if !FEATURE_IPV4
2+
#error [NOT_SUPPORTED] IPV4 not supported for this target
3+
#endif
4+
5+
#include "mbed.h"
6+
#include "EthernetInterface.h"
7+
#include "UDPSocket.h"
8+
#include "greentea-client/test_env.h"
9+
10+
namespace {
11+
//const char *HTTP_SERVER_NAME = "utcnist.colorado.edu";
12+
const char *HTTP_SERVER_NAME = "pool.ntp.org";
13+
const int HTTP_SERVER_PORT = 123;
14+
}
15+
16+
17+
int main() {
18+
GREENTEA_SETUP(20, "default_auto");
19+
20+
bool result = false;
21+
const time_t TIME1970 = 2208988800L;
22+
int ntp_values[12] = {0};
23+
24+
EthernetInterface eth;
25+
//eth.init(); //Use DHCP
26+
eth.connect();
27+
printf("UDP client IP Address is %s\n", eth.get_ip_address());
28+
29+
UDPSocket sock;
30+
sock.open(&eth);
31+
32+
SocketAddress nist(&eth, HTTP_SERVER_NAME, HTTP_SERVER_PORT);
33+
34+
printf("UDP: NIST server %s address: %s on port %d\r\n", HTTP_SERVER_NAME, nist.get_ip_address(), nist.get_port());
35+
36+
memset(ntp_values, 0x00, sizeof(ntp_values));
37+
ntp_values[0] = '\x1b';
38+
39+
int ret_send = sock.sendto(nist, (void*)ntp_values, sizeof(ntp_values));
40+
printf("UDP: Sent %d Bytes to NTP server \n", ret_send);
41+
42+
const int n = sock.recvfrom(&nist, (void*)ntp_values, sizeof(ntp_values));
43+
44+
printf("UDP: Recved from NTP server %d Bytes \n", n);
45+
46+
if (n > 0 ) {
47+
result = true;
48+
49+
printf("UDP: Values returned by NTP server: \n");
50+
for (size_t i=0; i < sizeof(ntp_values) / sizeof(ntp_values[0]); ++i) {
51+
printf("\t[%02d] 0x%X", i, ntohl(ntp_values[i]));
52+
53+
if (i == 10) {
54+
time_t timestamp = ntohl(ntp_values[i]) - TIME1970;
55+
printf("\tNTP timestamp is %s", ctime(&timestamp));
56+
} else {
57+
printf("\n");
58+
}
59+
}
60+
}
61+
62+
sock.close();
63+
eth.disconnect();
64+
GREENTEA_TESTSUITE_RESULT(result);
65+
}

0 commit comments

Comments
 (0)