# 使用 pthread 的 echo server (有bug! why?)
Author: WhoAmI
Date: 20230523
email:kccddb@gmail.com
Copyright: CC BY-NC-SA

**《荀子‧勸學》不積跬步,無以至千里;不積小流,無以成江海。**
**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)




[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)
:::

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