-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.c
135 lines (121 loc) · 4.85 KB
/
server.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// ---------- includes ---------- //
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include "common/io.h"
#include "common/mleak.h"
#include "common/myutil.h"
#include "common/string.h"
#include "server_h/client.h"
#include "server_h/global_var.h"
#include "server_h/struct.h"
// ---------- main ---------- //
int main(int argc, char *argv[]) {
atexit(mleak_finalize);
// まずはコマンドが正しく入力されているかを確認する
if (argc != 2) {
printf("%serror%s invalid number of arguments\n", FONT_RED, FONT_RESET);
printf("%sinfo%s usage: %s <port>\n", FONT_CYAN, FONT_RESET, argv[0]);
exit(EXIT_FAILURE);
}
int port = str2portNum(argv[1]);
if (port == -1) {
printf("%serror%s invalid port number: %s\n", FONT_RED, FONT_RESET, argv[1]);
exit(EXIT_FAILURE);
}
printf("%sinfo%s listening on port %d\n", FONT_CYAN, FONT_RESET, port);
// listening socketの作成
int listening_socket = socket(PF_INET, SOCK_STREAM, 0);
if (listening_socket == -1) {
printf("%serror%s socket creation failed\n", FONT_RED, FONT_RESET);
exit(EXIT_FAILURE);
}
// ソケットのオプション設定
if (setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, &(int) { 1 }, sizeof(int))) {
printf("%serror%s socket option setting failed\n", FONT_RED, FONT_RESET);
exit(EXIT_FAILURE);
}
// サーバー設定
struct sockaddr_in server;
memset((void *) &server, 0, sizeof(server));
server.sin_family = PF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
// Bind
if (bind(listening_socket, (struct sockaddr *) &server, sizeof(server)) == -1) {
printf("%serror%s bind failed\n", FONT_RED, FONT_RESET);
exit(EXIT_FAILURE);
}
// Listen
if (listen(listening_socket, MAX_CLIENTS) == -1) {
printf("%serror%s listen failed\n", FONT_RED, FONT_RESET);
exit(EXIT_FAILURE);
}
// グローバル変数の初期
string__init(&message.content);
string__init(&message.sender_name);
message.sender_id = -1;
message.message_id = -1;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
message.mutex = &mutex;
client_t clients[MAX_CLIENTS];
int num_clients = 0;
// 初期化
for (int i = 0; i < MAX_CLIENTS; i++) {
clients[i].addr = (struct sockaddr_in) { 0 };
clients[i].sock = -1;
string__from_cstr(&clients[i].name, "anonymous");
clients[i].send_thread = (pthread_t) { 0 };
clients[i].recv_thread = (pthread_t) { 0 };
clients[i].id = -1;
clients[i].last_message_id = -1;
clients[i].send_created = false;
clients[i].recv_created = false;
clients[i].send_terminated = false;
clients[i].recv_terminated = false;
}
// クライアント接続スレッド
pthread_t client_handler;
client_handler_arg client_handler_arg = { &num_clients, clients, &listening_socket };
if (pthread_create(&client_handler, NULL, handle_client, &client_handler_arg)) {
printf("%serror%s failed to create client handler thread\n", FONT_RED, FONT_RESET);
exit(EXIT_FAILURE);
}
bool client_handler_terminated = false;
// どれかが終了したか監視
while (true) {
bool any_terminated = false;
for (int i = 0; i < num_clients; i++) {
if (clients[i].send_terminated || clients[i].recv_terminated) {
any_terminated = true;
break;
}
}
if (any_terminated) break;
// もしclient_handlerが終了したらソケットを閉じる
if (!client_handler_terminated) {
if (pthread_tryjoin_np(client_handler, NULL) == 0) {
client_handler_terminated = true;
printf("%sinfo%s client handler thread terminated\n", FONT_CYAN, FONT_RESET);
close(listening_socket);
}
}
}
// 終了処理
printf("%sinfo%s closing server...\n", FONT_CYAN, FONT_RESET);
if (!client_handler_terminated && pthread_cancel(client_handler) != 0) printf("%swarn%s failed to cancel client handler thread\n", FONT_YELLOW, FONT_RESET);
if (!client_handler_terminated && pthread_join(client_handler, NULL) != 0) printf("%swarn%s failed to join client handler thread\n", FONT_YELLOW, FONT_RESET);
for (int i = 0; i < num_clients; i++) {
if ((clients[i].send_created && !clients[i].send_terminated) && pthread_join(clients[i].send_thread, NULL) != 0) printf("%swarn%s failed to join send thread for client %d\n", FONT_YELLOW, FONT_RESET, clients[i].id);
if ((clients[i].recv_created && !clients[i].recv_terminated) && pthread_join(clients[i].recv_thread, NULL) != 0) printf("%swarn%s failed to join recv thread for client %d\n", FONT_YELLOW, FONT_RESET, clients[i].id);
}
string__free(&message.content);
string__free(&message.sender_name);
if (!client_handler_terminated) close(listening_socket);
for (int i = 0; i < MAX_CLIENTS; i++) {
string__free(&clients[i].name);
if (clients[i].sock != -1) close(clients[i].sock);
}
exit(EXIT_SUCCESS);
}