|
|
@@ -0,0 +1,224 @@
|
|
|
+#include <time.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <signal.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <pthread.h>
|
|
|
+#include <arpa/inet.h>
|
|
|
+#include <sys/socket.h>
|
|
|
+
|
|
|
+const uint16_t SERVER_PORT = 1092;
|
|
|
+const int MAX_CLIENT_NUM = 20;
|
|
|
+const int MESSAGE_LEN = 256;
|
|
|
+const int HEADER_LEN = 4;
|
|
|
+
|
|
|
+int serv_sock;
|
|
|
+int clnt_socks[MAX_CLIENT_NUM];
|
|
|
+struct sockaddr_in clnt_addrs[MAX_CLIENT_NUM];
|
|
|
+
|
|
|
+struct message
|
|
|
+{
|
|
|
+ uint8_t opcode, status;
|
|
|
+ uint16_t length;
|
|
|
+ char data[MESSAGE_LEN - HEADER_LEN];
|
|
|
+};
|
|
|
+
|
|
|
+struct client
|
|
|
+{
|
|
|
+ uint16_t id, port;
|
|
|
+ uint32_t addr;
|
|
|
+};
|
|
|
+
|
|
|
+void send_message(int clnt_sock, uint8_t opcode, uint8_t status, uint16_t length, char data[])
|
|
|
+{
|
|
|
+ struct message msg;
|
|
|
+ msg.opcode = opcode;
|
|
|
+ msg.status = status;
|
|
|
+ msg.length = htons(length);
|
|
|
+ memcpy(msg.data, data, length);
|
|
|
+ write(clnt_sock, &msg, MESSAGE_LEN);
|
|
|
+}
|
|
|
+
|
|
|
+void *conn(void *id)
|
|
|
+{
|
|
|
+ uint16_t clnt_id = *(uint16_t *)id;
|
|
|
+ int clnt_sock = clnt_socks[clnt_id];
|
|
|
+
|
|
|
+ char data[MESSAGE_LEN];
|
|
|
+ sprintf(data, "Hello, client %hu.", clnt_id);
|
|
|
+ send_message(clnt_sock, 0, 1, strlen(data), data);
|
|
|
+
|
|
|
+ for (;;)
|
|
|
+ {
|
|
|
+ struct message msg;
|
|
|
+ int expect = MESSAGE_LEN;
|
|
|
+ for (char *p = (char *)&msg; expect > 0;)
|
|
|
+ {
|
|
|
+ int len = read(clnt_sock, p, expect);
|
|
|
+ if (len <= 0)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ expect -= len;
|
|
|
+ p += len;
|
|
|
+ }
|
|
|
+ if (expect > 0)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ printf("Receive message from client %hu. opcode = %hu.\n", clnt_id, msg.opcode);
|
|
|
+
|
|
|
+ switch (msg.opcode)
|
|
|
+ {
|
|
|
+ case 1:
|
|
|
+ {
|
|
|
+ time_t now;
|
|
|
+ time(&now);
|
|
|
+ strcpy(data, ctime(&now));
|
|
|
+ send_message(clnt_sock, 1, 1, strlen(data) - 1, data);
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case 2:
|
|
|
+ {
|
|
|
+ gethostname(data, MESSAGE_LEN);
|
|
|
+ send_message(clnt_sock, 2, 1, strlen(data), data);
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case 3:
|
|
|
+ {
|
|
|
+ int cnt = 0;
|
|
|
+ for (uint16_t i = 0; i < MAX_CLIENT_NUM; i++)
|
|
|
+ {
|
|
|
+ if (clnt_socks[i] != -1)
|
|
|
+ {
|
|
|
+ ((struct client *)data)[cnt].id = htons(i);
|
|
|
+ ((struct client *)data)[cnt].addr = clnt_addrs[i].sin_addr.s_addr;
|
|
|
+ ((struct client *)data)[cnt].port = clnt_addrs[i].sin_port;
|
|
|
+ cnt++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ send_message(clnt_sock, 3, 1, sizeof(struct client) * cnt, data);
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case 4:
|
|
|
+ {
|
|
|
+ uint16_t id = ntohs(((uint16_t *)msg.data)[0]);
|
|
|
+ if (clnt_socks[id] == -1)
|
|
|
+ {
|
|
|
+ send_message(clnt_sock, 4, 2, 0, NULL);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ((uint16_t *)msg.data)[0] = htons(clnt_id);
|
|
|
+ send_message(clnt_sock, 4, 1, 0, NULL);
|
|
|
+ send_message(clnt_socks[id], 0, 0, strlen(msg.data + 2) + 2, msg.data);
|
|
|
+ printf(" Forward message to client %hu.\n", id);
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ default:
|
|
|
+ {
|
|
|
+ sprintf(data, "Invalid operation.");
|
|
|
+ send_message(clnt_sock, 0, 2, strlen(data), data);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ close(clnt_sock);
|
|
|
+ printf("Client %hu disconnected.\n", clnt_id);
|
|
|
+ clnt_socks[clnt_id] = -1;
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+void stop_server(int sig)
|
|
|
+{
|
|
|
+ for (uint16_t i = 0; i < MAX_CLIENT_NUM; i++)
|
|
|
+ {
|
|
|
+ if (clnt_socks[i] != -1)
|
|
|
+ {
|
|
|
+ close(clnt_socks[i]);
|
|
|
+ printf("Client %hu disconnected.\n", i);
|
|
|
+ clnt_socks[i] = -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ close(serv_sock);
|
|
|
+ puts("Stop listening.");
|
|
|
+
|
|
|
+ exit(0);
|
|
|
+}
|
|
|
+
|
|
|
+int main()
|
|
|
+{
|
|
|
+ memset(clnt_socks, -1, sizeof(clnt_socks));
|
|
|
+
|
|
|
+ serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
+ if (serv_sock == -1)
|
|
|
+ {
|
|
|
+ puts("Create socket failed.");
|
|
|
+ exit(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ struct sockaddr_in serv_addr;
|
|
|
+ memset(&serv_addr, 0, sizeof(serv_addr));
|
|
|
+ serv_addr.sin_family = AF_INET;
|
|
|
+ serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
+ serv_addr.sin_port = htons(SERVER_PORT);
|
|
|
+ if (bind(serv_sock, (const struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
|
|
|
+ {
|
|
|
+ puts("Bind socket failed.");
|
|
|
+ exit(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (listen(serv_sock, MAX_CLIENT_NUM) == -1)
|
|
|
+ {
|
|
|
+ puts("Listen socket failed.");
|
|
|
+ exit(1);
|
|
|
+ }
|
|
|
+ puts("Start listening on port 1092...");
|
|
|
+
|
|
|
+ signal(SIGINT, stop_server);
|
|
|
+
|
|
|
+ for (;;)
|
|
|
+ {
|
|
|
+ struct sockaddr_in clnt_addr;
|
|
|
+ socklen_t clnt_addr_size = sizeof(clnt_addr);
|
|
|
+ int clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
|
|
|
+ if (clnt_sock == -1)
|
|
|
+ {
|
|
|
+ puts("Create socket failed.");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (uint16_t i = 0; i < MAX_CLIENT_NUM; i++)
|
|
|
+ {
|
|
|
+ if (clnt_socks[i] == -1)
|
|
|
+ {
|
|
|
+ clnt_socks[i] = clnt_sock;
|
|
|
+ clnt_addrs[i] = clnt_addr;
|
|
|
+
|
|
|
+ pthread_t pid;
|
|
|
+ if (pthread_create(&pid, NULL, conn, &i) != 0)
|
|
|
+ {
|
|
|
+ puts("Create thread failed.");
|
|
|
+ clnt_socks[i] = -1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ printf("Client %hu (%s:%hu) connected. pid = %lu.\n", i, inet_ntoa(clnt_addr.sin_addr), ntohs(clnt_addr.sin_port), pid);
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|