Skip to content

Conversation

@TORUS0818
Copy link
Owner

一旦動作確認が出来たのでPRを出します。

他の方々のPRのコメントを確認しましたが、

  • IPv6やホスト名での指定への対応
  • closeやexitの集約
  • 適度な関数切り出し

などに課題を感じました。

C言語に慣れておらず、都度キャッチアップしながら書いているので、おかしなところが多々あるかもしれません。
お作法等も併せてコメント頂けるとありがたいです。

Copy link

@hayashi-ay hayashi-ay left a comment

Choose a reason for hiding this comment

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

各システムコールと実際のTCP通信のフローがどう対応しているかなどは確認しておくと良いかなと思います。three way hand shakingはどれなど。必ずしもシステムコールと1対1で対応しているわけではないですが。

あとはrecv, send周りの処理は他の方のPRのコメントも参考にされると良いと思います。

あとは今のサーバーの実装はブロッキングになっている(1度に1つのクライアントとしか通信できない)と思うのでノンブロッキングにするとかですかね。selectシステムコールあたりを見てみると良いと思います。

client.c Outdated
#define BUFFER_SIZE (MESSAGE_SIZE + 1)

int main(int argc, char* argv[]) {
int sd;

Choose a reason for hiding this comment

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

あまり見ない命名な気がします。file descripterなのでfdとかですかね?

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 descripterのつもりでした。

client.c Outdated
fprintf(stderr, "invalid IP address.\n");
exit(EXIT_FAILURE);
}
if ((server_port = (unsigned short) atoi(argv[2])) == 0) {

Choose a reason for hiding this comment

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

atoiよりかはstrotolの方がoverflowとかも分かるので良いかなと思います。あとはポート番号はもう少しバリデーションしても良いかなと思います。ex. wellknow portは使わせないなど https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers

https://man7.org/linux/man-pages/man3/strtol.3.html

Copy link
Owner Author

Choose a reason for hiding this comment

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

参考資料もありがとうございます!

@TORUS0818
Copy link
Owner Author

ご指摘いただいた点を意識して、もう少し色々調べてみます!

Copy link

@inazz inazz left a comment

Choose a reason for hiding this comment

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

server/client 共通の話は片方にのみコメントしています。
IPv6に対応してみてください。
全てをメイン関数に書かず、適当な処理単位に分割し関数にして、名前を付けてください。
リモートからは任意のデータが飛んでくる可能性があると想定してください。最大長を仮定しないでください。

client.c Outdated
fprintf(stderr, "argument count missmatch.\n");
exit(EXIT_FAILURE);
}
if (inet_aton(argv[1], &server_sa.sin_addr) == 0) {
Copy link

Choose a reason for hiding this comment

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

ip v6 や、ホスト名が指定されても通信できるようにしてほしい。

Copy link
Owner Author

Choose a reason for hiding this comment

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

以下で修正しました。
0b9dca6

client.c Outdated
}

memset(&server_sa, 0, sizeof(server_sa));
server_sa.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.

これだと IPv4 のみになってしまうので、 v6 でも対応できるようにしたい。

Copy link
Owner Author

Choose a reason for hiding this comment

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

以下で修正しました。
0b9dca6

client.c Outdated
char receive_buffer[BUFFER_SIZE], send_buffer[BUFFER_SIZE];

if (argc != 3) {
fprintf(stderr, "argument count missmatch.\n");
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.

以下で修正しました。
752536b


while(1) {
printf("please enter the characters: ");
if (fgets(send_buffer, BUFFER_SIZE, stdin) == NULL) {
Copy link

Choose a reason for hiding this comment

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

入力の終了に対応してください (EOF -- 一般的には、端末で Ctrl-D を押した場合)

Copy link
Owner Author

@TORUS0818 TORUS0818 Oct 9, 2024

Choose a reason for hiding this comment

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

以下で修正しました。
fc9b5be

client.c Outdated
exit(EXIT_FAILURE);
}
// send
if (send(sd, send_buffer, strlen(send_buffer), 0) <= 0) {
Copy link

Choose a reason for hiding this comment

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

一部だけ送信に成功したケースを考慮してください。

client.c Outdated
int current_byte = 0;
while(current_byte < BUFFER_SIZE) {
// recv
received_byte = recv(sd, &receive_buffer[current_byte], 1, 0);
Copy link

Choose a reason for hiding this comment

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

システムコールを呼び出すと、
CPU割込みが発生し、権限を切り替え、ユーザの権限やパラメータチェックをし、必要に応じてロックを取ったりと、、、と、データを1バイトコピーするよりはるかに大きな労力がかかります。
まとまった単位で読み込むようにしてください。

Copy link
Owner Author

Choose a reason for hiding this comment

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

以下で修正しました。
fc9b5be

client.c Outdated
if (received_byte < 0) {
perror("recv() failed.\n");
exit(EXIT_FAILURE);
} else if (received_byte == 0) {
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.

以下で修正しました。
fc9b5be

client.c Outdated

if (receive_buffer[current_byte] == '\n') {
receive_buffer[current_byte] = '\0';
if (strcmp(receive_buffer, "quit") == 0) {
Copy link

Choose a reason for hiding this comment

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

receive_buffer に \0 が入っていない場合はバッファオーバーフローの危険性があります。
(実際には第2引数の5バイト目が\0なので起きないだろうとは推測しますが、strcmp は保証してくれない)

Copy link
Owner Author

Choose a reason for hiding this comment

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

以下で修正しました。(上記処理を廃止しました。)
fc9b5be

server.c Outdated

while(1) {
//recv
if ((receive_message_size = recv(client_sd, receive_buffer, BUFFER_SIZE, 0)) < 0) {
Copy link

Choose a reason for hiding this comment

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

recv も同様に一回で全部読めるとは保証されません。
また、先方から送られてくるデータがBUFFER_SIZEより多い場合はどうなるでしょう?

Copy link
Owner Author

@TORUS0818 TORUS0818 Oct 9, 2024

Choose a reason for hiding this comment

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

送られてきたデータがバッファより大きい場合には、動的にバッファを拡大して対応できるように実装できそうです。(対応予定)

client.c Outdated
printf("received from server: %s\n", receive_buffer);
}
return EXIT_SUCCESS;
} No newline at end of file
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.

以下で修正しました。
ee4e10c

@TORUS0818
Copy link
Owner Author

TORUS0818 commented Oct 9, 2024

残課題メモ:

  • ノンブロッキング対応(poll or selectを使う)
  • 初期バッファサイズを超えるデータ受信時の対応

@@ -0,0 +1,88 @@
#include <stdio.h>
Copy link

Choose a reason for hiding this comment

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

#include の順番に何か規則を持たせたい。 (<> -> "" の順番で、アルファベット順など)

n = send(s, buf + sent_total, len - sent_total, 0);
if (n == -1) {
perror("send() failed.\n");
exit(EXIT_FAILURE);
Copy link

Choose a reason for hiding this comment

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

もし失敗時に exit するなら、 send_all_or_die みたいな名前にしたい。

}
if (n == 0) {
printf("end of input detected (EOF).\n");
return sent_total;
Copy link

Choose a reason for hiding this comment

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

send_all なので、 paritial に終わらせたくない。
(partial な時に exit を呼ぶならエラー時と同じ挙動でまだ理解しやすいかも)

return received_total;
}
received_total += n;
if (strchr(buf, '\n') != NULL) {
Copy link

Choose a reason for hiding this comment

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

receive_all と書いてあるので、データの内容については触れたくない。

printf("server disconnected.\n");
break;
}
receive_buffer[receive_message_size] = '\0';
Copy link

Choose a reason for hiding this comment

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

受け取ったデータがバッファ全部に詰まっていた際に、これはバッファの次のメモリ (out of bounds) になってしまう。


while(1) {
//recv
receive_message_size = receive_all(client_sd, receive_buffer, BUFFER_SIZE);
Copy link

Choose a reason for hiding this comment

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

receive_all はエラー時に exit するけれど、
client の一つが通信エラー発生しただけでサーバーを終了させたくない。

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.

4 participants