-
Notifications
You must be signed in to change notification settings - Fork 0
implement minimum server and client #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
maeken4
wants to merge
2
commits into
main
Choose a base branch
from
feature/server
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| bin/* | ||
| build/* |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| CC := gcc | ||
| CFLAGS := -std=c11 -Wall -Wextra -O2 -g -I src -MMD -MP | ||
| LDFLAGS := | ||
| LDLIBS := | ||
|
|
||
| SRCDIR := src | ||
| OBJDIR := build | ||
| BINDIR := bin | ||
|
|
||
| SRCS_SERVER := $(SRCDIR)/http_server.c $(SRCDIR)/http_request.c | ||
| SRCS_CLIENT := $(SRCDIR)/http_client.c | ||
|
|
||
| OBJS_SERVER := $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SRCS_SERVER)) | ||
| OBJS_CLIENT := $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SRCS_CLIENT)) | ||
|
|
||
| TARGET_SERVER := $(BINDIR)/http_server | ||
| TARGET_CLIENT := $(BINDIR)/http_client | ||
|
|
||
| DEPS := $(OBJS_SERVER:.o=.d) $(OBJS_CLIENT:.o=.d) | ||
|
|
||
| .PHONY: all clean | ||
|
|
||
| all: $(TARGET_SERVER) $(TARGET_CLIENT) | ||
|
|
||
| # create bin and build dirs as order-only prerequisites | ||
| $(BINDIR): | ||
| mkdir -p $(BINDIR) | ||
|
|
||
| $(OBJDIR): | ||
| mkdir -p $(OBJDIR) | ||
|
|
||
| # pattern rule to compile .c -> build/%.o | ||
| $(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR) | ||
| $(CC) $(CFLAGS) -c $< -o $@ | ||
|
|
||
| # link targets | ||
| $(TARGET_SERVER): $(OBJS_SERVER) | $(BINDIR) | ||
| $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ | ||
|
|
||
| $(TARGET_CLIENT): $(OBJS_CLIENT) | $(BINDIR) | ||
| $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ | ||
|
|
||
| # include dependency files if present | ||
| -include $(DEPS) | ||
|
|
||
| clean: | ||
| rm -rf $(BINDIR) $(OBJDIR) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| # 📝課題: C言語 + system callでHTTP Serverを作ってみよう | ||
| ``` | ||
| GET /calc?query=2+10 HTTP/1.1 | ||
| ``` | ||
| に対して | ||
| ``` | ||
| HTTP/1.1 200 OK | ||
| Content-Length: 2 | ||
|
|
||
| 12 | ||
| ``` | ||
|
|
||
| を返すようなもの | ||
| ## ヒント | ||
| - manを使う | ||
| - system callのエラーは必ず対処する | ||
| - メモリは動的に確保しよう | ||
| - クライアントとサーバーの処理が別で必要 | ||
|
|
||
| ## 余力があれば挑戦するとよいこと | ||
| - IPv4+v6両対応 | ||
| - CPU性能を最大限活用できるようにする | ||
| - non-blocking化 | ||
| - マルチスレッド化 | ||
| - 通信タイムアウトの設定 | ||
| - SSL対応 | ||
| - signalを受け取ったら全コネクションが正常終了するようにする | ||
|
|
||
|
|
||
|
|
||
| ### 自分の実装計画 | ||
|
|
||
| まず1プロセスで単純なHTTPサーバーを作る。 | ||
| - socket, bind, listen, acceptで通信確立 | ||
| - request lineの解析 | ||
| - headerの解析 | ||
| - bobyから計算 | ||
|
|
||
| 時間があったら... | ||
| CI/CDをちゃんと設定する | ||
| →次にワーカープロセスを使った実装 | ||
| →次にマルチスレッド | ||
| →次にepollによる実装 | ||
|
|
||
| ## 調べたもの | ||
|
|
||
| ### bind(2) | ||
| addrをsocketのfdに結び付ける | ||
| 0: success | ||
| -1: error | ||
|
|
||
| sockaddr_in構造体=ポート番号 | ||
|
|
||
| ### HTTP リクエスト | ||
| HTTPリクエストは、以下の3つの要素で構成される。 | ||
|
|
||
| - リクエスト行 | ||
| - ヘッダーフィールド | ||
| - ボディ | ||
| ヘッダーフィールド、ボディは省略可能。 | ||
| ヘッダーフィールドとボディの間は、空行を1行挟む。 | ||
|
|
||
| https://qiita.com/gunso/items/94c1ce1e53c282bc8d2f#2http%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%81%AE%E6%A7%8B%E6%88%90 | ||
| https://www.rfc-editor.org/rfc/rfc9112.html | ||
|
|
||
|
|
||
| ### HTTP レスポンス | ||
| - HTTPリクエストと同様に、 | ||
| ``` | ||
| POST /index.html HTTP/1.0 | ||
| ``` | ||
| みたいにスペースで区切られる | ||
|
|
||
| - headerフィールド | ||
| フィールド名:valueの形式 | ||
|
|
||
| - HTTPレスポンスは、以下の3つの要素で構成される。 | ||
| - ステータス行 | ||
| - ヘッダーフィールド | ||
| - ボディ | ||
|
|
||
| ### C10k問題 | ||
| プロセス作れる数の限界UNIX系ではプロセス数が~32767, 16bit=2^16 | ||
|
|
||
| ### bindとTIME_WAITについて | ||
| TCPにおいて、切断後、一定時間はソケット状態を維持するため、アプリを再起動したときにすぐに同じポートを再利用できない。`SO_REUSEADDR`を設定するとうまくいく | ||
| ``` | ||
| // bindが解放されなくなる対策 | ||
| const int one = 1; | ||
| // 第2引数で設定したいレイヤー、第3引数でそのレイヤーで設定したいオプション、第4引数でそのオプションの値を入れる。 | ||
| setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); | ||
| ``` | ||
| https://qiita.com/bamchoh/items/1dd44ba1fbef43b5284b | ||
| https://bombrary.github.io/blog/posts/socket01-file-tcp/ | ||
|
|
||
| ## 頂いたコメント | ||
| - clientからhttp_requestを送るときにconnectまで成功して、いざsend使用する直前に切断されてしまった場合、そのままsendするとSIGPIPEシグナルが飛んできてプロセスが終了してしまう。 | ||
| >サーバー側のコネクションがcloseしているとここでプロセスが落ちるのでMSG_NOSIGNALをつけてベットエラーハンドリングをした方が良いかもしれないです。 | ||
|
|
||
| >MSG_NOSIGNAL (Linux 2.2 以降) | ||
| ストリーム指向のソケットで相手側が接続を切断した時に、エラーとして SIGPIPE を送信しないように要求する。 | ||
| https://ja.manpages.org/send/2 | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| #define _GNU_SOURCE | ||
| #include <errno.h> | ||
| #include <netdb.h> | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
| #include <sys/socket.h> | ||
| #include <sys/types.h> | ||
| #include <unistd.h> | ||
|
|
||
| #define SERVER_ADDR "127.0.0.1" | ||
| #define SERVER_PORT "8090" | ||
|
|
||
| int main() { | ||
| struct addrinfo hints, *result, *res_p; | ||
| int err, client_fd = -1; | ||
|
|
||
| memset(&hints, 0, sizeof(struct addrinfo)); | ||
| hints.ai_family = AF_INET; // IPv4 | ||
| hints.ai_socktype = SOCK_STREAM; // TCP | ||
|
|
||
| err = getaddrinfo(SERVER_ADDR, SERVER_PORT, &hints, &result); | ||
| if (err != 0) { | ||
| fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err)); | ||
| exit(EXIT_FAILURE); | ||
| } | ||
|
|
||
| for (res_p = result; res_p; res_p = res_p->ai_next) { | ||
| client_fd = socket(res_p->ai_family, res_p->ai_socktype, res_p->ai_protocol); | ||
| if (client_fd < 0) { | ||
| fprintf(stderr, "create socket failed: %s (errno=%d)\n", strerror(errno), errno); | ||
| continue; | ||
| } | ||
|
|
||
| if (connect(client_fd, res_p->ai_addr, res_p->ai_addrlen) < 0) { | ||
| fprintf(stderr, "create connection failed: %s (errno=%d)\n", strerror(errno), errno); | ||
| continue; | ||
| } | ||
| // connect成功 | ||
| break; | ||
| } | ||
| // TODO: ここいらない? | ||
| if (client_fd < 0) { | ||
| printf("create connection error"); | ||
| exit(EXIT_FAILURE); | ||
| } | ||
| freeaddrinfo(result); | ||
|
|
||
| // http requestの書き込み | ||
| char request[512]; | ||
| int request_len = snprintf(request, sizeof(request), | ||
| "GET /calc?query=12+24 HTTP/1.1\r\n" | ||
| "Host: %s:%s\r\n" | ||
| "User-Agent: simple-c-http-client/1.0\r\n" | ||
| "Connection: close\r\n" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. KeepAlive ではないのでConnection: closeをつけているのが丁寧で良いと思いました! |
||
| "\r\n", | ||
| SERVER_ADDR, SERVER_PORT); | ||
| // send: 送り切るまで | ||
| int sent = 0; | ||
| while (sent < request_len) { | ||
| int n = send(client_fd, request + sent, request_len - sent, MSG_NOSIGNAL); | ||
| if (n < 0) { | ||
| if (errno == EINTR) continue; // interrupted system call | ||
| perror("send"); | ||
| close(client_fd); | ||
| exit(1); | ||
| } | ||
| sent += n; | ||
| } | ||
|
|
||
| // http responseの読み取り | ||
| // TODO: Status line, header, bodyをちゃんと読み取るようにする | ||
| // TODO: 動的にメモリ確保をする | ||
| int BUF_SIZE = 500; | ||
| char buf[BUF_SIZE]; | ||
| // TODO: ここもBUFF_SIZE受け取れているとは限らない... | ||
| int response_len = recv(client_fd, buf, BUF_SIZE, 0); // BUF_SIZEより大きいと動かない | ||
|
Comment on lines
+75
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mallocで動的にメモリを確保してレスポンスを受け取る書き方も試しても良いかもしれません。 |
||
| buf[response_len] = '\0'; | ||
|
|
||
| close(client_fd); | ||
| printf("%s", buf); | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
サーバー側の名前が
http_server.cなのでhttp_client.cだとわかりやすいと思いました!