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 6666
40 #define INVALID_SOCKET -1
41 #define CLIENT_NUM 25
42 #define BACKLOG CLIENT_NUM
43
44 static int gFds[FD_SETSIZE];
45 static int gBye;
46
InitFds()47 static void InitFds()
48 {
49 for (int i = 0; i < FD_SETSIZE; ++i) {
50 gFds[i] = INVALID_SOCKET;
51 }
52 }
53
GetReadfds(fd_set * fds,int * nfd)54 static void GetReadfds(fd_set *fds, int *nfd)
55 {
56 for (int i = 0; i < FD_SETSIZE; i++) {
57 if (gFds[i] == INVALID_SOCKET) {
58 continue;
59 }
60 FD_SET(gFds[i], fds);
61 if (*nfd < gFds[i]) {
62 *nfd = gFds[i];
63 }
64 }
65 }
66
AddFd(int fd)67 static int AddFd(int fd)
68 {
69 for (int i = 0; i < FD_SETSIZE; ++i) {
70 if (gFds[i] == INVALID_SOCKET) {
71 gFds[i] = fd;
72 return 0;
73 }
74 }
75 return -1;
76 }
77
DelFd(int fd)78 static void DelFd(int fd)
79 {
80 for (int i = 0; i < FD_SETSIZE; ++i) {
81 if (gFds[i] == fd) {
82 gFds[i] = INVALID_SOCKET;
83 }
84 }
85 (void)close(fd);
86 }
87
CloseAllFd(void)88 static int CloseAllFd(void)
89 {
90 for (int i = 0; i < FD_SETSIZE; ++i) {
91 if (gFds[i] != INVALID_SOCKET) {
92 (void)close(gFds[i]);
93 gFds[i] = INVALID_SOCKET;
94 }
95 }
96 return 0;
97 }
98
HandleRecv(int fd)99 static int HandleRecv(int fd)
100 {
101 char buf[256];
102 int ret = recv(fd, buf, sizeof(buf)-1, 0);
103 if (ret < 0) {
104 LogPrintln("[%d]Error: %s", fd, strerror(errno));
105 DelFd(fd);
106 } else if (ret == 0) {
107 LogPrintln("[%d]Closed", fd);
108 DelFd(fd);
109 } else {
110 buf[ret] = 0;
111 LogPrintln("[%d]Received: %s", fd, buf);
112 if (strstr(buf, "Bye") != NULL) {
113 DelFd(fd);
114 gBye++;
115 }
116 }
117 return -(ret < 0);
118 }
119
HandleAccept(int lsfd)120 static int HandleAccept(int lsfd)
121 {
122 struct sockaddr_in sa;
123 int saLen = sizeof(sa);
124 int fd = accept(lsfd, (struct sockaddr *)&sa, (socklen_t *)&saLen);
125 if (fd == INVALID_SOCKET) {
126 perror("accept");
127 return -1;
128 }
129
130 if (AddFd(fd) == -1) {
131 LogPrintln("Too many clients, refuse %s:%d", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
132 close(fd);
133 return -1;
134 }
135 LogPrintln("New client %d: %s:%d", fd, inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
136 return 0;
137 }
138
HandleReadfds(fd_set * fds,int lsfd)139 static int HandleReadfds(fd_set *fds, int lsfd)
140 {
141 int ret = 0;
142 for (int i = 0; i < FD_SETSIZE; ++i) {
143 if (gFds[i] == INVALID_SOCKET || !FD_ISSET(gFds[i], fds)) {
144 continue;
145 }
146 if (gFds[i] == lsfd) {
147 ret += HandleAccept(lsfd);
148 } else {
149 ret += HandleRecv(gFds[i]);
150 }
151 }
152 return ret;
153 }
154
ClientsThread(void * param)155 static void *ClientsThread(void *param)
156 {
157 int fd;
158 int thrNo = (int)(intptr_t)param;
159
160 LogPrintln("<%d>socket client thread started", thrNo);
161 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
162 if (fd == INVALID_SOCKET) {
163 perror("socket");
164 return NULL;
165 }
166
167 struct sockaddr_in sa;
168 sa.sin_family = AF_INET;
169 sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
170 sa.sin_port = htons(SERVER_PORT);
171 if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
172 perror("connect");
173 return NULL;
174 }
175
176 LogPrintln("[%d]<%d>connected to %s:%d successful", fd, thrNo, inet_ntoa(sa.sin_addr), SERVER_PORT);
177
178 const char *msg[] = {
179 "hello, ",
180 "ohos, ",
181 "my name is net_socket_test_008, ",
182 "see u next time, ",
183 "Bye!"
184 };
185
186 for (int i = 0; i < sizeof(msg) / sizeof(msg[0]); ++i) {
187 if (send(fd, msg[i], strlen(msg[i]), 0) < 0) {
188 LogPrintln("[%d]<%d>send msg [%s] fail", fd, thrNo, msg[i]);
189 }
190 }
191
192 (void)shutdown(fd, SHUT_RDWR);
193 (void)close(fd);
194 return param;
195 }
196
StartClients(pthread_t * cli,int cliNum)197 static int StartClients(pthread_t *cli, int cliNum)
198 {
199 int ret;
200 pthread_attr_t attr;
201
202 for (int i = 0; i < cliNum; ++i) {
203 ret = pthread_attr_init(&attr);
204 ICUNIT_ASSERT_EQUAL(ret, 0, ret);
205
206 ret = pthread_create(&cli[i], &attr, ClientsThread, (void *)(intptr_t)i);
207 ICUNIT_ASSERT_EQUAL(ret, 0, ret);
208
209 ret = pthread_attr_destroy(&attr);
210 ICUNIT_ASSERT_EQUAL(ret, 0, ret);
211 }
212 return 0;
213 }
214
SelectTest(void)215 static int SelectTest(void)
216 {
217 struct sockaddr_in sa = {0};
218 int lsfd;
219 int ret;
220
221 lsfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
222 ICUNIT_ASSERT_NOT_EQUAL(lsfd, -1, errno);
223
224 sa.sin_family = AF_INET;
225 sa.sin_addr.s_addr = htonl(INADDR_ANY);
226 sa.sin_port = htons(SERVER_PORT);
227 ret = bind(lsfd, (struct sockaddr *)&sa, sizeof(sa));
228 ICUNIT_ASSERT_NOT_EQUAL(ret, -1, errno + CloseAllFd());
229
230 ret = listen(lsfd, BACKLOG);
231 ICUNIT_ASSERT_NOT_EQUAL(ret, -1, errno + CloseAllFd());
232
233 InitFds();
234 AddFd(lsfd);
235 LogPrintln("[%d]Waiting for client to connect on port %d", lsfd, SERVER_PORT);
236
237 pthread_t clients[CLIENT_NUM];
238
239 ret = StartClients(clients, CLIENT_NUM);
240 ICUNIT_ASSERT_EQUAL(ret, 0, ret + CloseAllFd());
241
242 for ( ; ; ) {
243 int nfd;
244 fd_set readfds;
245 struct timeval timeout;
246
247 timeout.tv_sec = 3;
248 timeout.tv_usec = 0;
249
250 nfd = 0;
251 FD_ZERO(&readfds);
252
253 GetReadfds(&readfds, &nfd);
254
255 ret = select(nfd + 1, &readfds, NULL, NULL, &timeout);
256 LogPrintln("select %d", ret);
257 if (ret == -1) {
258 perror("select");
259 break; // error occurred
260 } else if (ret == 0) {
261 break; // timed out
262 }
263
264 if (HandleReadfds(&readfds, lsfd) < 0) {
265 break;
266 }
267 }
268
269 for (int i = 0; i < CLIENT_NUM; ++i) {
270 ret = pthread_join(clients[i], NULL);
271 ICUNIT_ASSERT_EQUAL(ret, 0, ret + CloseAllFd());
272 }
273
274 ICUNIT_ASSERT_EQUAL(gBye, CLIENT_NUM, gBye + CloseAllFd());
275 (void)CloseAllFd();
276 return ICUNIT_SUCCESS;
277 }
278
NetSocketTest008(void)279 void NetSocketTest008(void)
280 {
281 TEST_ADD_CASE(__FUNCTION__, SelectTest, TEST_POSIX, TEST_TCP, TEST_LEVEL0, TEST_FUNCTION);
282 }
283