1 /*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 * conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 * of conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16 * to endorse or promote products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32
33 #include <osTest.h>
34 #include <sys/socket.h>
35 #include <sys/select.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38
39 #define SERVER_PORT 7777
40 #define INVALID_SOCKET -1
41 #define CLIENT_NUM 25
42
43 static int gFds[FD_SETSIZE];
44 static int gBye;
45
InitFds()46 static void InitFds()
47 {
48 for (int i = 0; i < FD_SETSIZE; ++i) {
49 gFds[i] = INVALID_SOCKET;
50 }
51 }
52
GetReadfds(fd_set * fds,int * nfd)53 static void GetReadfds(fd_set *fds, int *nfd)
54 {
55 for (int i = 0; i < FD_SETSIZE; i++) {
56 if (gFds[i] == INVALID_SOCKET) {
57 continue;
58 }
59 FD_SET(gFds[i], fds);
60 if (*nfd < gFds[i]) {
61 *nfd = gFds[i];
62 }
63 }
64 }
65
AddFd(int fd)66 static int AddFd(int fd)
67 {
68 for (int i = 0; i < FD_SETSIZE; ++i) {
69 if (gFds[i] == INVALID_SOCKET) {
70 gFds[i] = fd;
71 return 0;
72 }
73 }
74 return -1;
75 }
76
DelFd(int fd)77 static void DelFd(int fd)
78 {
79 for (int i = 0; i < FD_SETSIZE; ++i) {
80 if (gFds[i] == fd) {
81 gFds[i] = INVALID_SOCKET;
82 }
83 }
84 (void)close(fd);
85 }
86
CloseAllFd(void)87 static int CloseAllFd(void)
88 {
89 for (int i = 0; i < FD_SETSIZE; ++i) {
90 if (gFds[i] != INVALID_SOCKET) {
91 (void)close(gFds[i]);
92 gFds[i] = INVALID_SOCKET;
93 }
94 }
95 return 0;
96 }
97
HandleRecv(int fd)98 static int HandleRecv(int fd)
99 {
100 char buf[128];
101 int ret = recv(fd, buf, sizeof(buf)-1, 0);
102 if (ret < 0) {
103 LogPrintln("[%d]Error: %s", fd, strerror(errno));
104 DelFd(fd);
105 } else if (ret == 0) {
106 LogPrintln("[%d]Closed", fd);
107 DelFd(fd);
108 } else {
109 buf[ret] = 0;
110 LogPrintln("[%d]Received: %s", fd, buf);
111 if (strstr(buf, "Bye") != NULL) {
112 gBye++;
113 }
114 }
115 return -(ret < 0);
116 }
117
HandleReadfds(fd_set * fds,int lsfd)118 static int HandleReadfds(fd_set *fds, int lsfd)
119 {
120 int ret = 0;
121 for (int i = 0; i < FD_SETSIZE; ++i) {
122 if (gFds[i] == INVALID_SOCKET || !FD_ISSET(gFds[i], fds)) {
123 continue;
124 }
125 ret += HandleRecv(gFds[i]);
126 }
127 return ret;
128 }
129
ClientsThread(void * param)130 static void *ClientsThread(void *param)
131 {
132 int fd;
133 int thrNo = (int)(intptr_t)param;
134
135 LogPrintln("<%d>socket client thread started", thrNo);
136 fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
137 if (fd == INVALID_SOCKET) {
138 perror("socket");
139 return NULL;
140 }
141
142 struct sockaddr_in sa;
143 sa.sin_family = AF_INET;
144 sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
145 sa.sin_port = htons(SERVER_PORT);
146 if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
147 perror("connect");
148 return NULL;
149 }
150
151 LogPrintln("[%d]<%d>connected to udp://%s:%d successful", fd, thrNo, inet_ntoa(sa.sin_addr), SERVER_PORT);
152
153 const char *msg[] = {
154 "ohos, ",
155 "hello, ",
156 "my name is net_socket_test_011, ",
157 "see u next time, ",
158 "Bye!",
159 };
160
161 for (int i = 0; i < sizeof(msg) / sizeof(msg[0]); ++i) {
162 if (send(fd, msg[i], strlen(msg[i]), 0) < 0) {
163 LogPrintln("[%d]<%d>send msg [%s] fail", fd, thrNo, msg[i]);
164 }
165 }
166
167 (void)shutdown(fd, SHUT_RDWR);
168 (void)close(fd);
169 return param;
170 }
171
StartClients(pthread_t * cli,int cliNum)172 static int StartClients(pthread_t *cli, int cliNum)
173 {
174 int ret;
175 pthread_attr_t attr = {0};
176 struct sched_param param = { 0 };
177 int policy;
178 ret = pthread_getschedparam(pthread_self(), &policy, ¶m);
179 ICUNIT_ASSERT_EQUAL(ret, 0, -ret);
180
181 for (int i = 0; i < cliNum; ++i) {
182 ret = pthread_attr_init(&attr);
183 param.sched_priority = param.sched_priority + 1;
184 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
185 pthread_attr_setschedparam(&attr, ¶m);
186
187 ret = pthread_create(&cli[i], &attr, ClientsThread, (void *)(intptr_t)i);
188 ICUNIT_ASSERT_EQUAL(ret, 0, ret);
189 ret = pthread_attr_destroy(&attr);
190 ICUNIT_ASSERT_EQUAL(ret, 0, ret);
191 }
192 return 0;
193 }
194
UdpSelectTest(void)195 static int UdpSelectTest(void)
196 {
197 struct sockaddr_in sa = {0};
198 int lsfd;
199 int ret;
200
201 lsfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
202 ICUNIT_ASSERT_NOT_EQUAL(lsfd, -1, errno);
203
204 sa.sin_family = AF_INET;
205 sa.sin_addr.s_addr = htonl(INADDR_ANY);
206 sa.sin_port = htons(SERVER_PORT);
207 ret = bind(lsfd, (struct sockaddr *)&sa, sizeof(sa));
208 ICUNIT_ASSERT_NOT_EQUAL(ret, -1, errno + CloseAllFd());
209
210 InitFds();
211 AddFd(lsfd);
212 LogPrintln("[%d]Waiting for client to connect on UDP port %d", lsfd, SERVER_PORT);
213
214 pthread_t clients[CLIENT_NUM];
215
216 ret = StartClients(clients, CLIENT_NUM);
217 ICUNIT_ASSERT_EQUAL(ret, 0, ret + CloseAllFd());
218
219 for ( ; ; ) {
220 int nfd;
221 fd_set readfds;
222 struct timeval timeout;
223
224 timeout.tv_sec = 3;
225 timeout.tv_usec = 0;
226
227 nfd = 0;
228 FD_ZERO(&readfds);
229
230 GetReadfds(&readfds, &nfd);
231
232 ret = select(nfd + 1, &readfds, NULL, NULL, &timeout);
233 LogPrintln("select %d", ret);
234 if (ret == -1) {
235 perror("select");
236 break; // error occurred
237 } else if (ret == 0) {
238 break; // timed out
239 }
240
241 if (HandleReadfds(&readfds, lsfd) < 0) {
242 break;
243 }
244 }
245
246 for (int i = 0; i < CLIENT_NUM; ++i) {
247 ret = pthread_join(clients[i], NULL);
248 ICUNIT_ASSERT_EQUAL(ret, 0, ret + CloseAllFd());
249 }
250
251 ICUNIT_ASSERT_EQUAL(gBye, CLIENT_NUM, gBye + CloseAllFd());
252 (void)CloseAllFd();
253 return ICUNIT_SUCCESS;
254 }
255
NetSocketTest011(void)256 void NetSocketTest011(void)
257 {
258 TEST_ADD_CASE(__FUNCTION__, UdpSelectTest, TEST_POSIX, TEST_TCP, TEST_LEVEL0, TEST_FUNCTION);
259 }
260