# 使用 pthread 的 echo server (有bug! why?) Author: WhoAmI Date: 20230523 email:kccddb@gmail.com Copyright: CC BY-NC-SA ![](https://i.imgur.com/7S2PHTx.png) **《荀子‧勸學》不積跬步,無以至千里;不積小流,無以成江海。** **I. Mutex variables MUST be declared with type pthread_mutex_t, and must be initialized before they can be used.** II. 了解 pthread_mutex_lock / pthread_mutex_unlock 與可能問題和代價 III. 程式執行效率與startvation 等問題 [POSIX thread (pthread) libraries](https://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html) ![](https://i.imgur.com/LnH1H3O.jpg) ![](https://i.imgur.com/vt1lppl.jpg) ![](https://i.imgur.com/Re4xtlA.jpg) ![](https://i.imgur.com/QHum1I6.jpg) [Multithreaded Programming (POSIX pthreads Tutorial)](https://randu.org/tutorials/threads/) 這是一個使用pthread 的簡單 echo server! 請用 putty 測試, 您可能測試正常! **但是事實上 有 BUG!!!** **但是這是有問題的server! 不穩定! 當您連線的 client 很快時就可能會出錯! Why?** **HINT:** //Get Client's FD 可能出問題! Why? 如何修改? 因此server 的網路程式必須更僅慎! 如有必要需壓力測試! [pthread_create - create a new thread](https://man7.org/linux/man-pages/man3/pthread_create.3.html) int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void *), void *restrict arg); :::info Compile and link with -pthread. 特別小心 void *restrict arg ::: [pthread_join - join with a terminated thread](https://man7.org/linux/man-pages/man3/pthread_join.3.html) [pthread_detach - detach a thread](https://man7.org/linux/man-pages/man3/pthread_detach.3.html) [ pthread_exit - terminate calling thread](https://man7.org/linux/man-pages/man3/pthread_exit.3.html) int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); **注意 有 BUG!!!!** 請找出來, 還有何時可能有問題? 如何修改? Hint: malloc ```c= //BUG! simple TCP server #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> #include <sys/socket.h> #include <resolv.h> #include <arpa/inet.h> #include <pthread.h> void PANIC(char* msg); #define PANIC(msg) { perror(msg); exit(EXIT_FAILURE); } #define SERVER_PORT 22222 void* ServerThread(void* arg) { char buf[100]; int bytes_read; int fd = *(int *)arg; //Get Client's FD for(;;) { bytes_read = recv(fd, buf, sizeof(buf), 0); send(fd, buf, bytes_read, 0); if(strncmp(buf, "q", 1)==0)break; } printf("Connection Close!\n"); close(fd); return arg; } int main(void) { int fd; struct sockaddr_in server_addr; pthread_t my_thread; int client_fd, addr_size; if ( (fd= socket(PF_INET, SOCK_STREAM, 0)) < 0 ) PANIC("Socket"); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = INADDR_ANY; if ( bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr))!= 0 )PANIC("bind"); if ( listen(fd, 20)!=0 )PANIC("listen"); for(;;) { addr_size = sizeof(server_addr); client_fd = accept(fd, (struct sockaddr *)&server_addr, &addr_size); printf("Connected: %s:%d\n", inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port)); if ( pthread_create(&my_thread, NULL, ServerThread, &client_fd) != 0 ) //unstable! FIXIT! perror("Thread creation"); else pthread_detach(my_thread); } exit(0); } ``` 修正 上面的 bug 請自行 補上 setsockopt SO_REUSEADDR 的處理 與 SIGPIPE signal 的處理 ```c= #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> #include <sys/socket.h> #include <resolv.h> #include <arpa/inet.h> #include <pthread.h> void PANIC(char* msg); #define PANIC(msg) { perror(msg); exit(EXIT_FAILURE); } #define SERVER_PORT 22222 void* ServerThread(void* arg) { char buf[256]; int bytes_read; int *fd = (int *)arg; //Get Client's FD printf("Client %d\n",*fd); for(;;) { bytes_read = recv(*fd, buf, sizeof(buf)-1, 0); if(bytes_read<=0)break; buf[bytes_read]=0; printf("read: %d bytes:%s\n", bytes_read, buf); send(*fd, buf, bytes_read, 0); if(strncmp(buf, "q", 1)==0)break; } printf("Connection Close!\n"); close(*fd); free(fd); return arg; } int main(void) { int fd; struct sockaddr_in server_addr; pthread_t my_thread; int *client_fd, addr_size; if ( (fd= socket(PF_INET, SOCK_STREAM, 0)) < 0 ) PANIC("Socket"); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = INADDR_ANY; if ( bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr))!= 0 )PANIC("bind"); if ( listen(fd, 20)!=0 )PANIC("listen"); for(;;) { addr_size = sizeof(server_addr); client_fd = malloc(sizeof (int)); *client_fd = accept(fd, (struct sockaddr *)&server_addr, &addr_size); printf("Connected: %s:%d\n", inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port)); if ( pthread_create(&my_thread, NULL, ServerThread, client_fd) != 0 ) //unstable! FIXIT! perror("Thread creation"); else pthread_detach(my_thread); } exit(0); } ``` #define LONGSTRING(...) #__VA_ARGS__ 簡單 pthread 版 web server ```c= /* *Author: WhoAmI *Date: 20230523 * */ //Simple HTTP server #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> #include <sys/socket.h> #include <resolv.h> #include <arpa/inet.h> #include <pthread.h> void PANIC(char* msg); #define PANIC(msg) { perror(msg); exit(EXIT_FAILURE); } #define SERVER_PORT 22222 #define INFO(EXP) \ do { if (EXP) \ fprintf (stderr, "Warning: " #EXP "=%d\n",EXP); } \ while (0) #define LONGSTRING(...) #__VA_ARGS__ const char *indexhtml2=LONGSTRING(HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\r\n\r\n<html><body>Welcome</body> </html>); const char *indexhtml = LONGSTRING(HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\r\n\r\n <!DOCTYPE html> <html> <style> table, th, td { border:1px solid black; } </style> <body> <h2>A basic HTML table</h2> <table style="width:100%"> <tr> <th>Company</th> <th>Contact</th> <th>Name</th> </tr> <tr> <td>John </td> <td>Maria</td> <td>Joe</td> </tr> <tr> <td>FoodPanda</td> <td>Chang</td> <td>Beer</td> </tr> </table> <p>we have added borders to the table.</p> </body> </html> ); #define debugx(x) static void sig_pipe(int signum) { printf(" Broken Pipe!\n"); } static void HandleSignal(void){ //handle all signals signal(SIGPIPE, sig_pipe); } void* ServerThread(void* arg) { char buf[2048]; int bytes_read; int len=0; int *fd = (int *)arg; //Get Client's FD printf("Client %d\n",*fd); for(;;) { bytes_read = read(*fd, buf, sizeof(buf)-1); if(bytes_read<=0)break; buf[bytes_read]=0; INFO(bytes_read); len=strlen(indexhtml); debugx(printf("read: %d bytes:\n%s\n", bytes_read, buf)); write(*fd, indexhtml, len+1); debugx(printf("%s",indexhtml)); if(strncmp(buf, "q", 1)==0)break; break; } printf("Connection Close!\n"); close(*fd); free(fd); return arg; } int main(void) { int fd; struct sockaddr_in server_addr; int yes=1; pthread_t my_thread; int *client_fd, addr_size; HandleSignal(); if ( (fd= socket(PF_INET, SOCK_STREAM, 0)) < 0 ) PANIC("Socket"); if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) PANIC("setsockopt"); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = INADDR_ANY; if ( bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr))!= 0 )PANIC("bind"); if ( listen(fd, 20)!=0 )PANIC("listen"); for(;;) { addr_size = sizeof(server_addr); client_fd = malloc(sizeof (int)); printf("Waiting\n"); *client_fd = accept(fd, (struct sockaddr *)&server_addr, &addr_size); printf("Connected: %s:%d\n", inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port)); if ( pthread_create(&my_thread, NULL, ServerThread, client_fd) != 0 ) //unstable! FIXIT! perror("Thread creation"); else pthread_detach(my_thread); } exit(0); } ``` :::info Futher Reading: [Pthread Scheduling, Fairness, Power, Performance](https://hackmd.io/@pingulinux/Pthread-Scheduling-Fairness-Power) [Circular Buffer](https://hackmd.io/@pingulinux/CircularBuffer) ::: ![](https://i.imgur.com/FhyvCsm.jpg) HW. double linked list (不考慮排序), thread 2,3 write new data, thread1 read and delete data, 您如何設計? ![](https://i.imgur.com/XrsenFK.jpg)