From d32a9977c39e0cc239cfd6068b2556292e11189f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=9B=E6=9C=88=E4=BD=B3=E5=BD=A6?= Date: Thu, 3 Oct 2024 11:42:04 +0900 Subject: [PATCH 01/13] =?UTF-8?q?Add:=20client.c=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 client.c diff --git a/client.c b/client.c new file mode 100644 index 0000000..5052b5e --- /dev/null +++ b/client.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include + +#define MESSAGE_SIZE 1024 +#define BUFFER_SIZE (MESSAGE_SIZE + 1) + +int main(int argc, char* argv[]) { + int sd; + struct sockaddr_in server_sa; + unsigned short server_port; + char receive_buffer[BUFFER_SIZE], send_buffer[BUFFER_SIZE]; + + if (argc != 3) { + fprintf(stderr, "argument count missmatch.\n"); + exit(EXIT_FAILURE); + } + if (inet_aton(argv[1], &server_sa.sin_addr) == 0) { + fprintf(stderr, "invalid IP address.\n"); + exit(EXIT_FAILURE); + } + if ((server_port = (unsigned short) atoi(argv[2])) == 0) { + fprintf(stderr, "invalid port number.\n"); + exit(EXIT_FAILURE); + } + + memset(&server_sa, 0, sizeof(server_sa)); + server_sa.sin_family = AF_INET; + server_sa.sin_port = htons(server_port); + + // socket + if ((sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + perror("socket() failed.\n"); + exit(EXIT_FAILURE); + } + // connect + if (connect(sd, (struct sockaddr*) &server_sa, sizeof(server_sa))) { + perror("connect() failed.\n"); + exit(EXIT_FAILURE); + } + printf("connect to %s.\n", inet_ntoa(server_sa.sin_addr)); + + while(1) { + printf("please enter the characters: "); + if (fgets(send_buffer, BUFFER_SIZE, stdin) == NULL) { + fprintf(stderr, "invalid input.\n"); + exit(EXIT_FAILURE); + } + // send + if (send(sd, send_buffer, strlen(send_buffer), 0) <= 0) { + perror("send() failed.\n"); + exit(EXIT_FAILURE); + } + + int received_byte = 0; + int current_byte = 0; + while(current_byte < BUFFER_SIZE) { + // recv + received_byte = recv(sd, &receive_buffer[current_byte], 1, 0); + if (received_byte < 0) { + perror("recv() failed.\n"); + exit(EXIT_FAILURE); + } else if (received_byte == 0) { + perror("ERR_EMPTY_RESPONSE"); + exit(EXIT_FAILURE); + } + + if (receive_buffer[current_byte] == '\n') { + receive_buffer[current_byte] = '\0'; + if (strcmp(receive_buffer, "quit") == 0) { + close(sd); + return EXIT_SUCCESS; + } + break; + } + current_byte += received_byte; + } + printf("received from server: %s\n", receive_buffer); + } + return EXIT_SUCCESS; +} \ No newline at end of file From 8db084ba2d629edc674e4d3ec9c6d5df548e477c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=9B=E6=9C=88=E4=BD=B3=E5=BD=A6?= Date: Thu, 3 Oct 2024 11:45:29 +0900 Subject: [PATCH 02/13] =?UTF-8?q?Add:=20server.c=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 server.c diff --git a/server.c b/server.c new file mode 100644 index 0000000..5bb6b88 --- /dev/null +++ b/server.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include + +#define QUEUE_LIMIT 5 +#define MESSAGE_SIZE 1024 +#define BUFFER_SIZE (MESSAGE_SIZE + 1) + +int main(int argc, char* argv[]) { + int client_sd, server_sd; + struct sockaddr_in client_sa, server_sa; + unsigned short server_port; + unsigned int client_len; + char receive_buffer[BUFFER_SIZE]; + int receive_message_size, send_message_size; + + if (argc != 2) { + fprintf(stderr, "argument count missmatch.\n"); + exit(EXIT_FAILURE); + } + if ((server_port = (unsigned short) atoi(argv[1])) == 0) { + fprintf(stderr, "invalid port number.\n"); + exit(EXIT_FAILURE); + } + // socket + if ((server_sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + perror("socket() failed.\n"); + exit(EXIT_FAILURE); + } + + memset(&server_sa, 0, sizeof(server_sa)); + server_sa.sin_family = AF_INET; + server_sa.sin_addr.s_addr = htonl(INADDR_ANY); + server_sa.sin_port = htons(server_port); + + // bind + if (bind(server_sd, (struct sockaddr*) &server_sa, sizeof(server_sa)) < 0) { + perror("bind() failed.\n"); + exit(EXIT_FAILURE); + } + // listen + if (listen(server_sd, QUEUE_LIMIT) < 0) { + perror("listen() failed.\n"); + exit(EXIT_FAILURE); + } + + while(1) { + // accept + if ((client_sd = accept(server_sd, (struct sockaddr*) &client_sa, &client_len)) < 0) { + perror("accept() failed.\n"); + exit(EXIT_FAILURE); + } + printf("connected from %s.\n", inet_ntoa(client_sa.sin_addr)); + + while(1) { + //recv + if ((receive_message_size = recv(client_sd, receive_buffer, BUFFER_SIZE, 0)) < 0) { + perror("recv() failed.\n"); + exit(EXIT_FAILURE); + } else if(receive_message_size == 0) { + fprintf(stderr, "connection has already closed.\n"); + break; + } + //send + if ((send_message_size = send(client_sd, receive_buffer, receive_message_size, 0)) < 0) { + perror("send() failed.\n"); + exit(EXIT_FAILURE); + } else if(send_message_size == 0) { + fprintf(stderr, "connection has already closed.\n"); + break; + } + } + close(client_sd); + } + close(server_sd); + + return EXIT_SUCCESS; +} \ No newline at end of file From f554a06b54c384659a78ca1aa75ab736eae45c98 Mon Sep 17 00:00:00 2001 From: TORUS <42745810+TORUS0818@users.noreply.github.com> Date: Thu, 3 Oct 2024 18:55:54 +0900 Subject: [PATCH 03/13] Update README.md --- README.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 23ec05f..46d075e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,20 @@ -# socket-programming -CSZAP2 +## 概要 +echoサーバーを実装しています。 + +## 要件 +- C言語で実装 +- TCPで通信 +- ちゃんとエラーハンドリングをする +- 余裕があれば、 + - スレッドを使って高速化 + - シグナルでサーバ停止 + +## 使い方 +サーバーの起動: +```terminal +./server +``` +クライアントの起動: +```terminal +./client +``` From b7359881205909ece728c05406472e874ed85e23 Mon Sep 17 00:00:00 2001 From: TORUS <42745810+TORUS0818@users.noreply.github.com> Date: Thu, 3 Oct 2024 18:58:55 +0900 Subject: [PATCH 04/13] Create memo.md --- docs/memo.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/memo.md diff --git a/docs/memo.md b/docs/memo.md new file mode 100644 index 0000000..19d14da --- /dev/null +++ b/docs/memo.md @@ -0,0 +1 @@ +調べたことを適宜メモする From 752536b73c9f036a32e5de7f25203aba1107f720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=9B=E6=9C=88=E4=BD=B3=E5=BD=A6?= Date: Mon, 7 Oct 2024 11:04:29 +0900 Subject: [PATCH 05/13] =?UTF-8?q?Clean:=20=E5=BC=95=E6=95=B0=E3=81=AB?= =?UTF-8?q?=E9=96=A2=E3=81=99=E3=82=8B=E3=82=A8=E3=83=A9=E3=83=BC=E3=83=A1?= =?UTF-8?q?=E3=83=83=E3=82=BB=E3=83=BC=E3=82=B8=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.c | 2 +- server.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 5052b5e..746d776 100644 --- a/client.c +++ b/client.c @@ -15,7 +15,7 @@ int main(int argc, char* argv[]) { char receive_buffer[BUFFER_SIZE], send_buffer[BUFFER_SIZE]; if (argc != 3) { - fprintf(stderr, "argument count missmatch.\n"); + fprintf(stderr, "usage: ./client \n"); exit(EXIT_FAILURE); } if (inet_aton(argv[1], &server_sa.sin_addr) == 0) { diff --git a/server.c b/server.c index 5bb6b88..0c7f594 100644 --- a/server.c +++ b/server.c @@ -18,7 +18,7 @@ int main(int argc, char* argv[]) { int receive_message_size, send_message_size; if (argc != 2) { - fprintf(stderr, "argument count missmatch.\n"); + fprintf(stderr, "usage: ./server \n"); exit(EXIT_FAILURE); } if ((server_port = (unsigned short) atoi(argv[1])) == 0) { From ee4e10c24c10ab181d5e20299f4b4cd730fd445e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=9B=E6=9C=88=E4=BD=B3=E5=BD=A6?= Date: Mon, 7 Oct 2024 11:09:28 +0900 Subject: [PATCH 06/13] =?UTF-8?q?Clean:=20=E6=94=B9=E8=A1=8C=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.c | 2 +- server.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 746d776..b29e1b1 100644 --- a/client.c +++ b/client.c @@ -81,4 +81,4 @@ int main(int argc, char* argv[]) { printf("received from server: %s\n", receive_buffer); } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/server.c b/server.c index 0c7f594..f688b7d 100644 --- a/server.c +++ b/server.c @@ -78,4 +78,4 @@ int main(int argc, char* argv[]) { close(server_sd); return EXIT_SUCCESS; -} \ No newline at end of file +} From 0b9dca67c778ea404b5361ba7753e4e47553ddb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=9B=E6=9C=88=E4=BD=B3=E5=BD=A6?= Date: Mon, 7 Oct 2024 16:19:17 +0900 Subject: [PATCH 07/13] =?UTF-8?q?Update:=20=E3=83=97=E3=83=AD=E3=83=88?= =?UTF-8?q?=E3=82=B3=E3=83=AB=E9=9D=9E=E4=BE=9D=E5=AD=98=E3=81=AA=E5=AE=9F?= =?UTF-8?q?=E8=A3=85=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.c | 55 ++++++++++++++++++++++++++---------------- server.c | 73 +++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 80 insertions(+), 48 deletions(-) diff --git a/client.c b/client.c index b29e1b1..8a513a3 100644 --- a/client.c +++ b/client.c @@ -4,44 +4,57 @@ #include #include #include +#include #define MESSAGE_SIZE 1024 #define BUFFER_SIZE (MESSAGE_SIZE + 1) int main(int argc, char* argv[]) { int sd; - struct sockaddr_in server_sa; - unsigned short server_port; - char receive_buffer[BUFFER_SIZE], send_buffer[BUFFER_SIZE]; + int g; + char* node_name; + char* service_name; + struct addrinfo hints; + struct addrinfo* ai0; + struct addrinfo* ai; + char receive_buffer[BUFFER_SIZE]; + char send_buffer[BUFFER_SIZE]; if (argc != 3) { fprintf(stderr, "usage: ./client \n"); exit(EXIT_FAILURE); } - if (inet_aton(argv[1], &server_sa.sin_addr) == 0) { - fprintf(stderr, "invalid IP address.\n"); - exit(EXIT_FAILURE); - } - if ((server_port = (unsigned short) atoi(argv[2])) == 0) { - fprintf(stderr, "invalid port number.\n"); - exit(EXIT_FAILURE); - } + node_name = argv[1]; + service_name = argv[2]; - memset(&server_sa, 0, sizeof(server_sa)); - server_sa.sin_family = AF_INET; - server_sa.sin_port = htons(server_port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; - // socket - if ((sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { - perror("socket() failed.\n"); + g = getaddrinfo(node_name, service_name, &hints, &ai0); + if (g) { + fprintf(stderr, "%s", gai_strerror(g)); exit(EXIT_FAILURE); } - // connect - if (connect(sd, (struct sockaddr*) &server_sa, sizeof(server_sa))) { - perror("connect() failed.\n"); + for (ai = ai0; ai; ai = ai->ai_next) { + // socket + sd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sd < 0) { + continue; + } + // connect + if (connect(sd, ai->ai_addr, ai->ai_addrlen)) { + close(sd); + sd = -1; + continue; + } + break; + } + if (sd < 0) { + fprintf(stderr, "cannot connect %s.\n", node_name); exit(EXIT_FAILURE); } - printf("connect to %s.\n", inet_ntoa(server_sa.sin_addr)); + printf("connect to %s.\n", node_name); while(1) { printf("please enter the characters: "); diff --git a/server.c b/server.c index f688b7d..b6f6d0f 100644 --- a/server.c +++ b/server.c @@ -4,57 +4,76 @@ #include #include #include +#include #define QUEUE_LIMIT 5 #define MESSAGE_SIZE 1024 #define BUFFER_SIZE (MESSAGE_SIZE + 1) int main(int argc, char* argv[]) { - int client_sd, server_sd; - struct sockaddr_in client_sa, server_sa; - unsigned short server_port; - unsigned int client_len; + int client_sd; + int server_sd; + int g; + char* service_name; + struct addrinfo hints; + struct addrinfo* ai0; + struct addrinfo* ai; + struct sockaddr_storage ss; + unsigned int ss_len; char receive_buffer[BUFFER_SIZE]; - int receive_message_size, send_message_size; + int receive_message_size; + int send_message_size; if (argc != 2) { fprintf(stderr, "usage: ./server \n"); exit(EXIT_FAILURE); } - if ((server_port = (unsigned short) atoi(argv[1])) == 0) { - fprintf(stderr, "invalid port number.\n"); - exit(EXIT_FAILURE); - } - // socket - if ((server_sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { - perror("socket() failed.\n"); + service_name = argv[1]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + g = getaddrinfo(NULL, service_name, &hints, &ai0); + if (g) { + fprintf(stderr, "%s", gai_strerror(g)); exit(EXIT_FAILURE); } - memset(&server_sa, 0, sizeof(server_sa)); - server_sa.sin_family = AF_INET; - server_sa.sin_addr.s_addr = htonl(INADDR_ANY); - server_sa.sin_port = htons(server_port); - - // bind - if (bind(server_sd, (struct sockaddr*) &server_sa, sizeof(server_sa)) < 0) { - perror("bind() failed.\n"); - exit(EXIT_FAILURE); + for (ai = ai0; ai; ai = ai->ai_next) { + // socket + server_sd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (server_sd < 0) { + continue; + } + // bind + if (bind(server_sd, ai->ai_addr, ai->ai_addrlen) < 0) { + perror("bind() failed.\n"); + close(server_sd); + continue; + } + //listen + if (listen(server_sd, QUEUE_LIMIT) < 0) { + perror("listen() failed.\n"); + close(server_sd); + continue; + } + break; } - // listen - if (listen(server_sd, QUEUE_LIMIT) < 0) { - perror("listen() failed.\n"); + + if (server_sd < 0) { + fprintf(stderr, "cannot create server socket.\n"); exit(EXIT_FAILURE); } while(1) { // accept - if ((client_sd = accept(server_sd, (struct sockaddr*) &client_sa, &client_len)) < 0) { + if ((client_sd = accept(server_sd, (struct sockaddr*) &ss, &ss_len)) < 0) { perror("accept() failed.\n"); exit(EXIT_FAILURE); } - printf("connected from %s.\n", inet_ntoa(client_sa.sin_addr)); - + while(1) { //recv if ((receive_message_size = recv(client_sd, receive_buffer, BUFFER_SIZE, 0)) < 0) { From 4423a0bb6085a58710c4a8ebb317f6aecab22f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=9B=E6=9C=88=E4=BD=B3=E5=BD=A6?= Date: Mon, 7 Oct 2024 23:47:18 +0900 Subject: [PATCH 08/13] =?UTF-8?q?Clean:=20=E8=A1=A8=E8=A8=98=E3=81=AE?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.c | 18 +++++++++--------- server.c | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/client.c b/client.c index 8a513a3..3f9057e 100644 --- a/client.c +++ b/client.c @@ -9,14 +9,14 @@ #define MESSAGE_SIZE 1024 #define BUFFER_SIZE (MESSAGE_SIZE + 1) -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { int sd; - int g; - char* node_name; - char* service_name; + int status; + char *node_name; + char *service_name; struct addrinfo hints; - struct addrinfo* ai0; - struct addrinfo* ai; + struct addrinfo *ai0; + struct addrinfo *ai; char receive_buffer[BUFFER_SIZE]; char send_buffer[BUFFER_SIZE]; @@ -31,9 +31,9 @@ int main(int argc, char* argv[]) { hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - g = getaddrinfo(node_name, service_name, &hints, &ai0); - if (g) { - fprintf(stderr, "%s", gai_strerror(g)); + status = getaddrinfo(node_name, service_name, &hints, &ai0); + if (status) { + fprintf(stderr, "%s", gai_strerror(status)); exit(EXIT_FAILURE); } for (ai = ai0; ai; ai = ai->ai_next) { diff --git a/server.c b/server.c index b6f6d0f..19b4498 100644 --- a/server.c +++ b/server.c @@ -10,14 +10,14 @@ #define MESSAGE_SIZE 1024 #define BUFFER_SIZE (MESSAGE_SIZE + 1) -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { int client_sd; int server_sd; - int g; - char* service_name; + int status; + char *service_name; struct addrinfo hints; - struct addrinfo* ai0; - struct addrinfo* ai; + struct addrinfo *ai0; + struct addrinfo *ai; struct sockaddr_storage ss; unsigned int ss_len; char receive_buffer[BUFFER_SIZE]; @@ -35,9 +35,9 @@ int main(int argc, char* argv[]) { hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; - g = getaddrinfo(NULL, service_name, &hints, &ai0); - if (g) { - fprintf(stderr, "%s", gai_strerror(g)); + status = getaddrinfo(NULL, service_name, &hints, &ai0); + if (status) { + fprintf(stderr, "%s", gai_strerror(status)); exit(EXIT_FAILURE); } From 2cf0ad877c18ee80e186acd535b66b3690174195 Mon Sep 17 00:00:00 2001 From: TORUS <42745810+TORUS0818@users.noreply.github.com> Date: Tue, 8 Oct 2024 21:27:47 +0900 Subject: [PATCH 09/13] Update memo.md --- docs/memo.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/memo.md b/docs/memo.md index 19d14da..2560db0 100644 --- a/docs/memo.md +++ b/docs/memo.md @@ -1 +1,25 @@ 調べたことを適宜メモする + +## 参考資料 +- https://beej.us/guide/bgnet/html/#sendrecv + +## Tips +### atoiとstrtol +- atoi + - https://man7.org/linux/man-pages/man3/atol.3.html +- strtol + - https://man7.org/linux/man-pages/man3/strtol.3.html +- 以下のような理由で、strtolを利用した方が良い + - https://www.gavo.t.u-tokyo.ac.jp/~dsk_saito/lecture/software2/misc/misc02.html + - 整数以外の文字列が入力された時にエラーを検知できない + - intの範囲を大きく超える整数の検出などもできない +- atoiに与える文字列が完全に管理できるなら、strtolより軽量(エラーチェックなし、10進数しか扱えない)なのでメリットもある + +### 3ハンドシェイクとソケット関数との対応 +| 3ウェイハンドシェイクのステップ | ソケット関数 | 説明 | +| ---- | ---- | ---- | +| 1. SYN(接続要求) | connect() | サーバーにSYNを送り、3ウェイハンドシェイクを開始 | +| 2. SYN-ACK(応答) | accept() | サーバーが接続要求に応答(SYN-ACK)し、接続を確立 | +| 3. ACK(確認応答、接続完了) | accept() | クライアントからACKを受信し、通信準備が整う | + +## 用語集 From fc9b5be7b27d34c429770a749fe1a720ddb6ea44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=9B=E6=9C=88=E4=BD=B3=E5=BD=A6?= Date: Wed, 9 Oct 2024 18:42:54 +0900 Subject: [PATCH 10/13] =?UTF-8?q?Update:=20=E9=80=81=E5=8F=97=E4=BF=A1?= =?UTF-8?q?=E3=81=AE=E5=88=B6=E5=BE=A1=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.c | 52 +++++++++++++++++++++------------------------------- common.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ common.h | 6 ++++++ server.c | 30 +++++++++++++----------------- 4 files changed, 86 insertions(+), 48 deletions(-) create mode 100644 common.c create mode 100644 common.h diff --git a/client.c b/client.c index 3f9057e..d9b3d6a 100644 --- a/client.c +++ b/client.c @@ -6,8 +6,9 @@ #include #include -#define MESSAGE_SIZE 1024 -#define BUFFER_SIZE (MESSAGE_SIZE + 1) +#include "common.h" + +#define BUFFER_SIZE 1024 int main(int argc, char *argv[]) { int sd; @@ -17,6 +18,7 @@ int main(int argc, char *argv[]) { struct addrinfo hints; struct addrinfo *ai0; struct addrinfo *ai; + int receive_message_size; char receive_buffer[BUFFER_SIZE]; char send_buffer[BUFFER_SIZE]; @@ -36,6 +38,7 @@ int main(int argc, char *argv[]) { fprintf(stderr, "%s", gai_strerror(status)); exit(EXIT_FAILURE); } + for (ai = ai0; ai; ai = ai->ai_next) { // socket sd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); @@ -59,39 +62,26 @@ int main(int argc, char *argv[]) { while(1) { printf("please enter the characters: "); if (fgets(send_buffer, BUFFER_SIZE, stdin) == NULL) { - fprintf(stderr, "invalid input.\n"); - exit(EXIT_FAILURE); - } - // send - if (send(sd, send_buffer, strlen(send_buffer), 0) <= 0) { - perror("send() failed.\n"); - exit(EXIT_FAILURE); - } - - int received_byte = 0; - int current_byte = 0; - while(current_byte < BUFFER_SIZE) { - // recv - received_byte = recv(sd, &receive_buffer[current_byte], 1, 0); - if (received_byte < 0) { - perror("recv() failed.\n"); - exit(EXIT_FAILURE); - } else if (received_byte == 0) { - perror("ERR_EMPTY_RESPONSE"); - exit(EXIT_FAILURE); - } - - if (receive_buffer[current_byte] == '\n') { - receive_buffer[current_byte] = '\0'; - if (strcmp(receive_buffer, "quit") == 0) { - close(sd); - return EXIT_SUCCESS; - } + if (feof(stdin)) { + printf("end of input detected (EOF).\n"); break; + } else { + perror("error reading from stdin"); + exit(EXIT_FAILURE); } - current_byte += received_byte; } + // send + send_all(sd, send_buffer, strlen(send_buffer)); + // receive + receive_message_size = receive_all(sd, receive_buffer, BUFFER_SIZE); + if (receive_message_size == 0) { + printf("server disconnected.\n"); + break; + } + receive_buffer[receive_message_size] = '\0'; printf("received from server: %s\n", receive_buffer); } + close(sd); + return EXIT_SUCCESS; } diff --git a/common.c b/common.c new file mode 100644 index 0000000..952a127 --- /dev/null +++ b/common.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include + +ssize_t send_all(int s, char *buf, size_t len) { + int sent_total = 0; + int n; + + while (sent_total < len) { + n = send(s, buf + sent_total, len - sent_total, 0); + if (n == -1) { + perror("send() failed.\n"); + exit(EXIT_FAILURE); + } + if (n == 0) { + printf("end of input detected (EOF).\n"); + return sent_total; + } + sent_total += n; + } + return sent_total; +} + +ssize_t receive_all(int s, char *buf, size_t len) { + int received_total = 0; + int n; + + while (received_total < len) { + n = recv(s, buf + received_total, len - received_total, 0); + if (n == -1) { + perror("recv() failed.\n"); + exit(EXIT_FAILURE); + } + if (n == 0) { + printf("end of input detected (EOF).\n"); + return received_total; + } + received_total += n; + if (strchr(buf, '\n') != NULL) { + break; + } + } + return received_total; +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..94cf5ec --- /dev/null +++ b/common.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +ssize_t send_all(int s, char *buf, size_t len); +ssize_t receive_all(int s, char *buf, size_t len); diff --git a/server.c b/server.c index 19b4498..ff26931 100644 --- a/server.c +++ b/server.c @@ -6,9 +6,10 @@ #include #include +#include "common.h" + #define QUEUE_LIMIT 5 -#define MESSAGE_SIZE 1024 -#define BUFFER_SIZE (MESSAGE_SIZE + 1) +#define BUFFER_SIZE 1024 int main(int argc, char *argv[]) { int client_sd; @@ -20,9 +21,9 @@ int main(int argc, char *argv[]) { struct addrinfo *ai; struct sockaddr_storage ss; unsigned int ss_len; - char receive_buffer[BUFFER_SIZE]; - int receive_message_size; int send_message_size; + int receive_message_size; + char receive_buffer[BUFFER_SIZE]; if (argc != 2) { fprintf(stderr, "usage: ./server \n"); @@ -61,7 +62,6 @@ int main(int argc, char *argv[]) { } break; } - if (server_sd < 0) { fprintf(stderr, "cannot create server socket.\n"); exit(EXIT_FAILURE); @@ -73,28 +73,24 @@ int main(int argc, char *argv[]) { perror("accept() failed.\n"); exit(EXIT_FAILURE); } - + while(1) { //recv - if ((receive_message_size = recv(client_sd, receive_buffer, BUFFER_SIZE, 0)) < 0) { - perror("recv() failed.\n"); - exit(EXIT_FAILURE); - } else if(receive_message_size == 0) { - fprintf(stderr, "connection has already closed.\n"); + receive_message_size = receive_all(client_sd, receive_buffer, BUFFER_SIZE); + if (receive_message_size == 0) { + printf("connection has already closed.\n"); break; } //send - if ((send_message_size = send(client_sd, receive_buffer, receive_message_size, 0)) < 0) { - perror("send() failed.\n"); - exit(EXIT_FAILURE); - } else if(send_message_size == 0) { - fprintf(stderr, "connection has already closed.\n"); + send_message_size = send_all(client_sd, receive_buffer, receive_message_size); + if(send_message_size == 0) { + printf("connection has already closed.\n"); break; } } close(client_sd); } close(server_sd); - + return EXIT_SUCCESS; } From 3dc20fab8aa8359961aae61974368d8b5a8c270d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=9B=E6=9C=88=E4=BD=B3=E5=BD=A6?= Date: Wed, 9 Oct 2024 21:57:44 +0900 Subject: [PATCH 11/13] =?UTF-8?q?Clean:=20=E5=A4=89=E6=95=B0=E5=90=8D?= =?UTF-8?q?=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/client.c b/client.c index d9b3d6a..31643c0 100644 --- a/client.c +++ b/client.c @@ -11,7 +11,7 @@ #define BUFFER_SIZE 1024 int main(int argc, char *argv[]) { - int sd; + int s; int status; char *node_name; char *service_name; @@ -41,19 +41,19 @@ int main(int argc, char *argv[]) { for (ai = ai0; ai; ai = ai->ai_next) { // socket - sd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (sd < 0) { + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s < 0) { continue; } // connect - if (connect(sd, ai->ai_addr, ai->ai_addrlen)) { - close(sd); - sd = -1; + if (connect(s, ai->ai_addr, ai->ai_addrlen)) { + close(s); + s = -1; continue; } break; } - if (sd < 0) { + if (s < 0) { fprintf(stderr, "cannot connect %s.\n", node_name); exit(EXIT_FAILURE); } @@ -71,9 +71,9 @@ int main(int argc, char *argv[]) { } } // send - send_all(sd, send_buffer, strlen(send_buffer)); + send_all(s, send_buffer, strlen(send_buffer)); // receive - receive_message_size = receive_all(sd, receive_buffer, BUFFER_SIZE); + receive_message_size = receive_all(s, receive_buffer, BUFFER_SIZE); if (receive_message_size == 0) { printf("server disconnected.\n"); break; @@ -81,7 +81,7 @@ int main(int argc, char *argv[]) { receive_buffer[receive_message_size] = '\0'; printf("received from server: %s\n", receive_buffer); } - close(sd); + close(s); return EXIT_SUCCESS; } From 6dcf3044fe3c3a3a4ce848df4d78290c8f78a843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=9B=E6=9C=88=E4=BD=B3=E5=BD=A6?= Date: Thu, 10 Oct 2024 19:38:00 +0900 Subject: [PATCH 12/13] =?UTF-8?q?Fix:=20addrinfo=E3=82=92=E8=A7=A3?= =?UTF-8?q?=E6=94=BE=E3=81=99=E3=82=8B=E5=87=A6=E7=90=86=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.c | 1 + server.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/client.c b/client.c index 31643c0..0f011a2 100644 --- a/client.c +++ b/client.c @@ -58,6 +58,7 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } printf("connect to %s.\n", node_name); + freeaddrinfo(ai0); while(1) { printf("please enter the characters: "); diff --git a/server.c b/server.c index ff26931..fa7d8b6 100644 --- a/server.c +++ b/server.c @@ -66,6 +66,7 @@ int main(int argc, char *argv[]) { fprintf(stderr, "cannot create server socket.\n"); exit(EXIT_FAILURE); } + freeaddrinfo(ai0); while(1) { // accept @@ -91,6 +92,6 @@ int main(int argc, char *argv[]) { close(client_sd); } close(server_sd); - + return EXIT_SUCCESS; } From 213f58889eeaa63d496428fee65918fa938aefac Mon Sep 17 00:00:00 2001 From: TORUS <42745810+TORUS0818@users.noreply.github.com> Date: Thu, 10 Oct 2024 21:12:29 +0900 Subject: [PATCH 13/13] Update memo.md --- docs/memo.md | 113 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 4 deletions(-) diff --git a/docs/memo.md b/docs/memo.md index 2560db0..e2643cd 100644 --- a/docs/memo.md +++ b/docs/memo.md @@ -3,8 +3,8 @@ ## 参考資料 - https://beej.us/guide/bgnet/html/#sendrecv -## Tips -### atoiとstrtol +# Tips +## atoiとstrtol - atoi - https://man7.org/linux/man-pages/man3/atol.3.html - strtol @@ -15,11 +15,116 @@ - intの範囲を大きく超える整数の検出などもできない - atoiに与える文字列が完全に管理できるなら、strtolより軽量(エラーチェックなし、10進数しか扱えない)なのでメリットもある -### 3ハンドシェイクとソケット関数との対応 +## 3ハンドシェイクとソケット関数との対応 | 3ウェイハンドシェイクのステップ | ソケット関数 | 説明 | | ---- | ---- | ---- | | 1. SYN(接続要求) | connect() | サーバーにSYNを送り、3ウェイハンドシェイクを開始 | | 2. SYN-ACK(応答) | accept() | サーバーが接続要求に応答(SYN-ACK)し、接続を確立 | | 3. ACK(確認応答、接続完了) | accept() | クライアントからACKを受信し、通信準備が整う | -## 用語集 +## 主要なシステムコール +### getaddrinfo +```c +#include +#include +#include + +int getaddrinfo(const char *node, // e.g. "www.example.com" or IP + const char *service, // e.g. "http" or port number + const struct addrinfo *hints, + struct addrinfo **res); +``` +- 関連情報を記載したaddrinfo構造体(hints)へのポインタを渡すことで、それを補完した完全な状態のaddrinfoのリンクドリストを作成してくれる +- エラーは```gai_strerror()```で拾う +- 使い終わったリンクドリストは```freeaddrinfo()```で解放する +- ```AI_PASSIVE```は、ローカルホストのアドレスを割り当てる設定 + +### socket +```c +#include +#include + +int socket(int domain, int type, int protocol); +``` +- ソケットディスクリプタを返す +- domain = PF_INET / PF_INET6 +- type = SOCK_STREAM / SOCK_DGRAM +- 以下のように```getaddrinfo```で取得した情報を使って呼び出す +```c +getaddrinfo("www.example.com", "http", &hints, &res); +s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); +``` + +### bind +```c +#include +#include + +int bind(int sockfd, struct sockaddr *my_addr, int addrlen); +``` +- ソケットをポートに関連付けする +- 1024以下のポートは予約されているので使わないこと +- ポートの再利用を許可するよう、以下のように設定できる +```c +int yes=1; +//char yes='1'; // Solaris people use this + +// lose the pesky "Address already in use" error message +if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof yes) == -1) { + perror("setsockopt"); + exit(1); +} +``` + +### connect +```c +#include +#include + +int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); +``` +- 特定のIP、ポートに接続する +- ローカルのポートは気にしないので```bind```は呼ばない(カーネルが設定してくれる) + +### listen +```c +int listen(int sockfd, int backlog); +``` +- 接続を待つ +- backlogは、着信キューで許可されるコネクション数 + - 着信してきたコネクションは、```accept()```するまでこのキューで待機することになる + +### accept +```c +#include +#include + +int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +``` +- 接続を許可する +- 新しいソケットファイルディスクリプタが返される(こちらで```send/recv```する) + +### send +```c +int send(int sockfd, const void *msg, int len, int flags); +``` +- 送信したいデータへのポインタとデータのバイト長を渡すことで、データを送信できる +- 実際に送信されたバイト長を返す + - ```len```と一致しない場合は一部しか送信できていないことを意味する + +### recv +```c +int recv(int sockfd, void *buf, int len, int flags); +``` +- ```buf```に送られてきたデータを読み込む +- 0が返ってきた場合は、リモート側が接続を閉じたことを意味する + +### close +```c +close(sockfd); +``` +- ソケットディスクリプタの接続を閉じる +- ```shutdown```を使うとより細かい制御(受信、送信、送受信の拒否)ができる + - ```close```と異なり、ソケットディスクリプタを閉じるわけではないことに注意 + +# 用語集