Try   HackMD

使用 pthread 的 echo server (有bug! why?)

Author: WhoAmI
Date: 20230523
email:kccddb@gmail.com
Copyright: CC BY-NC-SA

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

《荀子‧勸學》不積跬步,無以至千里;不積小流,無以成江海。

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

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Multithreaded Programming (POSIX pthreads Tutorial)

這是一個使用pthread 的簡單 echo server!
請用 putty 測試, 您可能測試正常! 但是事實上 有 BUG!!!

但是這是有問題的server! 不穩定! 當您連線的 client 很快時就可能會出錯! Why?

HINT: //Get Client's FD 可能出問題! Why? 如何修改?

因此server 的網路程式必須更僅慎! 如有必要需壓力測試!
pthread_create - create a new thread

int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *),
void *restrict arg);

Compile and link with -pthread.
特別小心 void *restrict arg

pthread_join - join with a terminated thread

pthread_detach - detach a thread

pthread_exit - terminate calling thread

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

//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 的處理

#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

/* *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); }

HW. double linked list (不考慮排序), thread 2,3 write new data, thread1 read and delete data, 您如何設計?