Mercurial
comparison seobeo/main.c @ 3:2758f5527d2b
[Seobeo] Working on simple TCP server and client logic.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 24 Sep 2025 18:49:09 -0700 |
| parents | adcfad6e86fb |
| children | 0b3b4f5887bb |
comparison
equal
deleted
inserted
replaced
| 2:8a43dedbe530 | 3:2758f5527d2b |
|---|---|
| 1 /* | 1 /* |
| 2 ** server.c -- a stream socket server demo | 2 ** server.c -- a stream socket server demo |
| 3 */ | 3 */ |
| 4 | 4 |
| 5 #include "seobeo/seobeo.h" | 5 #include "seobeo/seobeo.h" |
| 6 | |
| 7 Dowa_PHashMap cache; | |
| 6 | 8 |
| 7 void SigchildHandler(int s) | 9 void SigchildHandler(int s) |
| 8 { | 10 { |
| 9 (void)s; // quiet unused variable warning | 11 (void)s; // quiet unused variable warning |
| 10 | 12 |
| 14 while(waitpid(-1, NULL, WNOHANG) > 0); | 16 while(waitpid(-1, NULL, WNOHANG) > 0); |
| 15 | 17 |
| 16 errno = saved_errno; | 18 errno = saved_errno; |
| 17 } | 19 } |
| 18 | 20 |
| 19 | |
| 20 void *GetInternetaddr(struct sockaddr *sa) | |
| 21 { | |
| 22 if (sa->sa_family == AF_INET) | |
| 23 { | |
| 24 return &(((struct sockaddr_in*)sa)->sin_addr); | |
| 25 } | |
| 26 | |
| 27 return &(((struct sockaddr_in6*)sa)->sin6_addr); | |
| 28 } | |
| 29 | |
| 30 void HandleClientRequest(int client_fd) | 21 void HandleClientRequest(int client_fd) |
| 31 { | 22 { |
| 32 FILE *file = fopen("seobeo/index.html", "rb"); | 23 size_t idx = Dowa_HashMap_GetPosition(cache, "index.html"); |
| 33 if (!file) { | 24 Dowa_PHashEntry entry = cache->entries[idx]; |
| 34 perror("fopen"); | 25 if (!entry) { |
| 26 // 404 response if missing | |
| 27 const char *not_found = | |
| 28 "HTTP/1.1 404 Not Found\r\n" | |
| 29 "Content-Length: 0\r\n" | |
| 30 "Connection: close\r\n" | |
| 31 "\r\n"; | |
| 32 send(client_fd, not_found, strlen(not_found), 0); | |
| 33 close(client_fd); | |
| 35 return; | 34 return; |
| 36 } | 35 } |
| 37 | 36 |
| 38 fseek(file, 0, SEEK_END); | 37 // 3) Prepare header with the correct contentālength |
| 39 size_t size = ftell(file); | 38 size_t body_size = entry->capacity; |
| 40 fseek(file, 0, SEEK_SET); | 39 const char *template = |
| 41 | |
| 42 char *data = malloc(size); | |
| 43 fread(data, 1, size, file); | |
| 44 fclose(file); | |
| 45 | |
| 46 char *header = malloc(100); | |
| 47 sprintf( | |
| 48 header, | |
| 49 "HTTP/1.1 200 OK\r\n" | 40 "HTTP/1.1 200 OK\r\n" |
| 50 "Content-Type: text/html\r\n" | 41 "Content-Type: text/html\r\n" |
| 51 "Content-Length: %zu\r\n" | 42 "Content-Length: %zu\r\n" |
| 52 "Connection: close\r\n" | 43 "Connection: close\r\n" |
| 53 "\r\n", | 44 "\r\n"; |
| 54 size | |
| 55 ); | |
| 56 | 45 |
| 57 send(client_fd, header, strlen(header), 0); | 46 // Compute how large the header is and allocate just enough space |
| 47 int header_len = snprintf(NULL, 0, template, body_size); | |
| 48 char *header = malloc(header_len + 1); | |
| 49 if (!header) { | |
| 50 perror("malloc"); | |
| 51 close(client_fd); | |
| 52 return; | |
| 53 } | |
| 54 snprintf(header, header_len + 1, template, body_size); | |
| 55 | |
| 56 // 4) Send header | |
| 57 send(client_fd, header, header_len, 0); | |
| 58 free(header); | |
| 59 | |
| 60 // 5) Stream the body in a loop until everything is sent | |
| 58 ssize_t total_sent = 0; | 61 ssize_t total_sent = 0; |
| 59 while (total_sent < size) | 62 const char *body = entry->buffer; |
| 63 while ((size_t)total_sent < body_size) | |
| 60 { | 64 { |
| 61 ssize_t sent = send(client_fd, data + total_sent, size - total_sent, 0); | 65 ssize_t sent = send( |
| 62 if (sent <= 0) break; | 66 client_fd, |
| 67 body + total_sent, | |
| 68 body_size - total_sent, | |
| 69 0 | |
| 70 ); | |
| 71 if (sent <= 0) | |
| 72 { | |
| 73 // error or connection closed by client | |
| 74 break; | |
| 75 } | |
| 63 total_sent += sent; | 76 total_sent += sent; |
| 64 } | 77 } |
| 65 | 78 |
| 79 // 6) Tear down | |
| 66 close(client_fd); | 80 close(client_fd); |
| 67 } | 81 } |
| 68 | 82 |
| 69 int main(void) | 83 int main(void) |
| 70 { | 84 { |
| 71 int32 client_fd; | 85 cache = Dowa_HashMap_Create(1024); |
| 72 struct sockaddr_storage client_addr; | 86 if (Dowa_Cache_Folder(cache, "seobeo/pages") != 0) |
| 73 socklen_t sin_size; | 87 { |
| 74 | 88 perror("Dowa_Cache_Folder"); |
| 75 char client_inet_addr[INET6_ADDRSTRLEN]; | 89 return -1; |
| 90 } | |
| 76 | 91 |
| 77 struct sigaction sa; | 92 struct sigaction sa; |
| 78 | 93 |
| 79 sa.sa_handler = SigchildHandler; // reap all dead processes | 94 sa.sa_handler = SigchildHandler; // reap all dead processes |
| 80 sigemptyset(&sa.sa_mask); | 95 sigemptyset(&sa.sa_mask); |
| 83 perror("sigaction"); | 98 perror("sigaction"); |
| 84 exit(1); | 99 exit(1); |
| 85 } | 100 } |
| 86 | 101 |
| 87 int sock_fd = Seobeo_CreateSocket(1, "6969", 10); | 102 int sock_fd = Seobeo_CreateSocket(1, "6969", 10); |
| 88 | 103 Seobeo_ListenClient(sock_fd, &HandleClientRequest); |
| 89 printf("server: waiting for connections...\n"); | |
| 90 | |
| 91 while (1) | |
| 92 { | |
| 93 sin_size = sizeof(client_addr); | |
| 94 client_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size); | |
| 95 | |
| 96 if (client_fd == -1) | |
| 97 { | |
| 98 perror("accept"); | |
| 99 break; | |
| 100 } | |
| 101 | |
| 102 inet_ntop( | |
| 103 client_addr.ss_family, | |
| 104 GetInternetaddr((struct sockaddr *)&client_addr), | |
| 105 client_inet_addr, sizeof client_inet_addr); | |
| 106 | |
| 107 printf("server: got connection from %s\n", client_inet_addr); | |
| 108 | |
| 109 // Create a child process | |
| 110 if (!fork()) | |
| 111 { | |
| 112 close(sock_fd); | |
| 113 HandleClientRequest(client_fd); | |
| 114 exit(0); | |
| 115 } | |
| 116 | |
| 117 close(client_fd); | |
| 118 } | |
| 119 | 104 |
| 120 return 0; | 105 return 0; |
| 121 } | 106 } |
| 122 | 107 |