1 | #include <unistd.h> |
2 | #include <netinet/in.h> |
3 | #include <sys/socket.h> |
4 | |
5 | #include "server.h" |
6 | #include "socket.h" |
7 | |
8 | #define CTEST_MAIN |
9 | #include "ctest.h" |
10 | |
11 | #include "common.h" |
12 | |
13 | cstr server_host = "127.0.0.1" ; |
14 | int server_port = 8181; |
15 | cstr content = "Lorem ipsum dolor sit amet" ; |
16 | |
17 | void process_data(vws_svr_data* req, void* thread_ctx) |
18 | { |
19 | vws_tcp_svr* server = req->server; |
20 | |
21 | vws.trace(VL_INFO, "process_data (%p)" , req); |
22 | |
23 | //> Prepare the response: echo the data back |
24 | |
25 | // Allocate memory for the data to be sent in response |
26 | char* data = (char*)vws.malloc(req->size); |
27 | |
28 | // Copy the request's data to the response data |
29 | strncpy(data, req->data, req->size); |
30 | |
31 | // Create response |
32 | vws_svr_data* reply = vws_svr_data_own( req->server, |
33 | req->cid, |
34 | (ucstr)data, |
35 | req->size ); |
36 | |
37 | // Free request |
38 | vws_svr_data_free(req); |
39 | |
40 | if (vws.tracelevel >= VT_APPLICATION) |
41 | { |
42 | vws.trace( VL_INFO, |
43 | "process_data(%lu): %i bytes" , |
44 | reply->cid, |
45 | reply->size); |
46 | } |
47 | |
48 | // Send reply. This will wakeup network thread. |
49 | vws_tcp_svr_send(reply); |
50 | } |
51 | |
52 | void server_thread(void* arg) |
53 | { |
54 | // Create a listening socket |
55 | int listenfd = socket(AF_INET, SOCK_STREAM, 0); |
56 | |
57 | ASSERT_TRUE(listenfd >= 0); |
58 | |
59 | // setsockopt: Handy debugging trick that lets us rerun the server |
60 | // immediately after we kill it; otherwise we have to wait about 20 secs. |
61 | // Eliminates "ERROR on binding: Address already in use" error. |
62 | int optval = 1; |
63 | setsockopt( listenfd, SOL_SOCKET, SO_REUSEADDR, |
64 | (const void*)&optval, sizeof(int) ); |
65 | |
66 | struct sockaddr_in serveraddr; |
67 | memset((char*)&serveraddr, 0, sizeof(serveraddr)); |
68 | serveraddr.sin_family = AF_INET; |
69 | serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); |
70 | serveraddr.sin_port = htons((unsigned short)server_port); |
71 | |
72 | struct sockaddr* bindaddr = (struct sockaddr*)&serveraddr; |
73 | ASSERT_TRUE(bind(listenfd, bindaddr, sizeof(serveraddr)) >= 0); |
74 | |
75 | vws_tcp_svr* server = (vws_tcp_svr*)arg; |
76 | server->state = VS_RUNNING; |
77 | server->on_data_in = process_data; |
78 | vws.tracelevel = VT_ALL; |
79 | server->trace = vws.tracelevel; |
80 | |
81 | ASSERT_TRUE(listen(listenfd, 10) >= 0); |
82 | |
83 | struct sockaddr_in clientaddr; |
84 | socklen_t clientlen = sizeof(clientaddr); |
85 | int connfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientlen); |
86 | ASSERT_TRUE(connfd >= 0); |
87 | |
88 | vws_tcp_svr_inetd_run(server, connfd); |
89 | } |
90 | |
91 | void client_thread(void* arg) |
92 | { |
93 | // Connect |
94 | vws.trace(VL_INFO, "[CLIENT] Connecting" ); |
95 | vws_socket* s = vws_socket_new(); |
96 | ASSERT_TRUE(vws_socket_connect(s, server_host, server_port, false)); |
97 | vws.trace(VL_INFO, "[CLIENT] Connected" ); |
98 | |
99 | // Send request |
100 | vws.trace(VL_INFO, "[CLIENT] Send: %s" , content); |
101 | vws_socket_write(s, (ucstr)content, strlen(content)); |
102 | |
103 | // Get reply |
104 | ssize_t n = vws_socket_read(s); |
105 | ASSERT_TRUE(n > 0); |
106 | vws.trace(VL_INFO, "[CLIENT] Receive: %s" , s->buffer->data); |
107 | |
108 | // Disconnect and cleanup. |
109 | vws_socket_free(s); |
110 | } |
111 | |
112 | CTEST(test_server, echo) |
113 | { |
114 | vws_tcp_svr* server = vws_tcp_svr_new(10, 0, 0); |
115 | vws.tracelevel = VT_THREAD; |
116 | server->on_data_in = process_data; |
117 | |
118 | vws.trace(VL_INFO, "[CLIENT] Starting server" ); |
119 | |
120 | uv_thread_t server_tid; |
121 | uv_thread_create(&server_tid, server_thread, server); |
122 | |
123 | // Wait for server to start up |
124 | while (server->state != VS_RUNNING) |
125 | { |
126 | vws_msleep(100); |
127 | } |
128 | |
129 | int nc = 1; |
130 | uv_thread_t* threads = vws.malloc(sizeof(uv_thread_t) * nc); |
131 | |
132 | for (int i = 0; i < nc; i++) |
133 | { |
134 | uv_thread_create(&threads[i], client_thread, NULL); |
135 | vws.trace(VL_INFO, "started client thread %p" , threads[i]); |
136 | } |
137 | |
138 | for (int i = 0; i < nc; i++) |
139 | { |
140 | uv_thread_join(&threads[i]); |
141 | vws.trace(VL_INFO, "stopped client thread %p" , threads[i]); |
142 | } |
143 | |
144 | free(threads); |
145 | |
146 | // Server auto-shutdown on client disconnect. Wait for thread. |
147 | uv_thread_join(&server_tid); |
148 | |
149 | // Cleanup |
150 | vws_tcp_svr_free(server); |
151 | } |
152 | |
153 | int main(int argc, const char* argv[]) |
154 | { |
155 | return ctest_main(argc, argv); |
156 | } |
157 | |