Skip to content

サーバー課題実装#1

Open
seal-azarashi wants to merge 20 commits intomainfrom
initial-implementation
Open

サーバー課題実装#1
seal-azarashi wants to merge 20 commits intomainfrom
initial-implementation

Conversation

@seal-azarashi
Copy link
Owner

レビューができるようサーバー課題の実装をプルリクエストにしています。

seal_azarashi added 2 commits October 2, 2024 15:32
@inazz
Copy link

inazz commented Oct 2, 2024

クライアントとサーバーで共通の問題は片方にしかコメントしていません。
バッファサイズを固定じゃなくしたいです。
また、大きな関数は読む (or メンテする)のに考えること、把握することが多くなり大変なので、
意味のある処理のまとまりを関数にまとめて名前を付けてみてください。

common_utils.h Outdated
#define BUFFER_SIZE 1024
#define LOOPBACK_ADDRESS "127.0.0.1"

void cleanup(int socket_fd)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ヘッダファイルにはプロトタイプ宣言だけを書いて、関数の実装は .c ファイルに書くのが一般的です。
.h は複数のファイルから include されるのが想定されます。
すると、実装が複数個作られることになり、リンクの際にどれを使えばよいかわからなくなりエラーになります。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご指摘ありがとうございます。理解しました。
同ファイルの関数はプロトタイプ宣言にし、実装を common_utils.c に記述するように修正しました。合わせて、実行コマンドが複雑になってきたので Makefile を作成しました: 9730d44


## その他気になったことメモ

- C では try catch で囲んでエラーが発生したら stack trace を出す、みたいなのは出来ないのだろうか?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C には例外機能は無いですが、(スタックの特定の構造を前提とした)バックトレースを取得する機能については、標準外ですが、 glibc 関数としてあったりはします。
https://www.gnu.org/software/libc/manual/html_node/Backtraces.html

client.c Outdated
int main()
{
struct sockaddr_in socket_address;
socket_address.sin_family = AF_INET;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IPv6 も対応するにはどうしたら良いでしょうか?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

socket_address を IPv6 対応に変更し、setsockopt を使用してオプションで IPv4 も受け付けるようにしました。
affc955

client.c Outdated
{
struct sockaddr_in socket_address;
socket_address.sin_family = AF_INET;
socket_address.sin_addr.s_addr = inet_addr(LOOPBACK_ADDRESS);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

接続先をコマンドライン引数で指定できるようにしてみてほしい。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正しました: 27232c2

client.c Outdated
if (connect(socket_fd, (struct sockaddr *)&socket_address, sizeof(socket_address)) == -1)
{
perror("connect failed");
cleanup(socket_fd);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cleanup, exit のコピペを減らすにはどういう構造にするとよいでしょうか?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

共通処理を何かしらの手段にひとつにまとめ、各所から呼び出すようにするのはどうかと考えました。
こちらでは関数にしています: dfa7ac9

Copy link
Owner Author

@seal-azarashi seal-azarashi Oct 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

メモ: goto を使うのもよくやるらしい (このようになにかのリソースを開放するような使い方が唯一許されている goto ぐらいの例)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

メモ: system call がどのような挙動をしたか等で処理を分けたくなった場合はさらに工夫が必要

client.c Outdated
}
else if (bytes_written < request_length)
{
fprintf(stderr, "Partial write occurred\n");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

一部分しか write できなかったのは、失敗ではなく、想定内の挙動です。(書き込み先のネットワークのバッファが足りなかった。書き込みの途中で割り込みを受けて一部完了で復帰した、など。)
その際にも正しく処理を続けられるコードにしてみてください。

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

manも確認しておくと良いですね。
https://man7.org/linux/man-pages/man2/write.2.html

The number of bytes written may be less than count if, for
example, there is insufficient space on the underlying physical
medium, or the RLIMIT_FSIZE resource limit is encountered (see
setrlimit(2)), or the call was interrupted by a signal handler
after having written less than count bytes. (See also pipe(7).)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read, write 合わせてこちらで修正しました: 405644d

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hayashi-ay さん、ドキュメントもありがとうございます。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

サーバーについてはこちらで修正しています: 461e118

client.c Outdated

printf("Sent HTTP GET request\n");

bytes_read = read(socket_fd, buffer, BUFFER_SIZE - 1);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read も一回で全部のデータが読めるとは限りません。
(データが複数パケットに分かれて、一つ目だけ到着した場合はどうなる?)
また、受け取ったデータ全体が buffer のサイズより大きいケースも考えてください。

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらも同様に。
https://man7.org/linux/man-pages/man2/read.2.html

It is not an error if this number is smaller than the number of
bytes requested; this may happen for example because fewer bytes
are actually available right now (maybe because we were close to
end-of-file, or because we are reading from a pipe, or from a
terminal), or because read() was interrupted by a signal.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read, write 合わせてこちらで修正しました: 405644d

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

サーバーについてはこちらで修正しています: 461e118

server.c Outdated
cleanup(socket_fd);
exit(EXIT_FAILURE);
}
if (listen(socket_fd, 3) == -1)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ちなみにbacklogのサイズを3にした理由はありますか?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

特に深い理由はなく、ちょっと負荷を与えたらすぐに許容できなくなる程度の値にしていました。
このままでいいのかについてはスレッド導入時に再検討する予定でいますので、その旨コメントに残しておきます: 51de367

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

まあ決めの問題かなとは思います。3というのが意味がありそうな値だったのと、第2引数がなにか把握されているか確認してみたくらいです。

server.c Outdated
}

cleanup(socket_fd);
return 0;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EXIT_SUCCESSを使ったらどうですか?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご指摘ありがとうございます。 global constants を把握出来ておりませんでした。こちら使うよう修正しました。
https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure?view=msvc-170

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

良いと思います。まあ好みではあると思いますが、自分が見たときの断面ではエラー時にはEXIT_FAILUREを使っていたのが気になったのでコメントしたくらいです。

Comment on lines +1 to +5
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include "common_utils.h"
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

何らかの規則性を持たせたい

  • alphabet 順
  • 独自定義のファイル等と標準ライブラリの間に一行空ける
  • etc.

Comment on lines +23 to +27
total_read += bytes_read;
if (strstr(buffer, "\r\n\r\n") != NULL) // ボディのない GET リクエストのみが来る想定
{
break;
}
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep-alive が来た時に変になる?
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Keep-Alive

socket_address.sin6_port = htons(atoi(PORT));

char buffer[BUFFER_SIZE] = {0};
char *http_request = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Host の値も 引数に合わせたい


printf("Sent HTTP GET request\n");

ssize_t total_received = receive_response(socket_fd, response, MAX_MESSAGE_SIZE - 1);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

意味的には、
MAX_MESSAGE_SIZE = BUFFER_SIZE - 1
として MAX_MESSAGE_SIZE を渡すようにする?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants