注:博主只关注编程实现的方面以及linux部分,部分网络原理讲解和windows实现部分跳过
第 18 章 多线程服务器端的实现
18.2 线程创建及运行
- c18p287:线程创建的 pthread_create 函数
1
2
3
4
5
6
7
8
9
10
11
int pthread_create(pthread_t * restrict thread,
const pthread_attr_t * restrict attr,
void *(* start_routine)(void *),
void * restrict arg);
// thread:保存新创建线程 ID 的变量地址值。
// attr:用于传递线程属性的参数,传递 NULL 时,创建默认属性的线程
// start_routine:相当于线程 main 函数的、
// 在单独执行流中执行的函数指针
// arg:通过第三个参数传递调用函数时包含传递参数信息的变量地址值
// 成功返回 0,失败返回其他值 - c18p187:线程创建的 pthread_create 函数的程序示例 thread1.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 这里的 arg 参数是 pthread_create 的第 4 个参数
void * thread_main(void * arg)
{
int i;
int cnt = *((int *)arg);
for (i = 0; i < cnt; ++i) {
sleep(1);
puts("running thread");
}
return NULL;
}
int main(int argc, char * argv[])
{
pthread_t t_id;
int thread_param = 5;
if (pthread_create(&t_id,
NULL,
thread_main,
(void *)&thread_param) != 0) {
puts("thread_create() error");
return -1;
}
// 为了延迟进程的终止时间
sleep(10);
puts("end of main");
return 0;
}- 编译运行
1
2
3
4
5
6
7
8shiqi@pc:~/network/ch18$ gcc thread1.c -o tr1 -lpthread
shiqi@pc:~/network/ch18$ ./tr1
running thread
running thread
running thread
running thread
running thread
end of main
- 编译运行
- c18p290:让调用线程的进程(或线程)等待的 pthread_join 函数
1
2
3
4
5
int pthread_join(pthread_t thread, void ** status);
// thread:该参数值 ID 的线程终止后才会从该函数返回
// status:保存线程的 main 函数返回值的指针变量地址值
// 成功返回 0,失败返回其他值- 调用该函数的进程(或线程)将进入等待状态,直到第一个参数为 ID 的线程终止为止,并且可以得到线程的 main 函数返回值
- c18p290:线程创建的 pthread_create + pthread_join 函数的程序示例 thread2.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void * thread_main(void * arg)
{
int i;
int cnt = *((int *)arg);
char * msg = (char *)malloc(sizeof(char)*50);
strcpy(msg, "hello, I'm thread ~\n");
for (i = 0; i < cnt; ++i) {
sleep(1);
puts("running thread");
}
return (void *)msg;
}
int main(int argc, char * argv[])
{
pthread_t t_id;
int thread_param = 5;
void * thr_ret;
if (pthread_create(&t_id,
NULL,
thread_main,
(void *)&thread_param) != 0) {
puts("pthread_create() error");
return -1;
}
// 注意此处获取返回值的方法
// 注意返回值是 thread_main 函数内部动态分配的内存空间地址值
if (pthread_join(t_id, &thr_ret) != 0) {
puts("pthread_join() error");
return -1;
}
printf("Thread return message: %s\n", (char *)thr_ret);
free(thr_ret);
return 0;
}- 编译运行
1
2
3
4
5
6
7
8shiqi@pc:~/network/ch18$ gcc thread2.c -o tr2 -lpthread
shiqi@pc:~/network/ch18$ ./tr2
running thread
running thread
running thread
running thread
running thread
Thread return message: hello, I'm thread ~
- 编译运行
- c18p292:如何通过加宏定义把平台定义的非线程安全函数改为线程安全函数?
- 头文件
#define _REENTRANT
或gcc -D_REENTRANT
- 头文件
- c18p293:工作线程模型示例程序 thread3.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int sum = 0;
void * thread_summation(void * arg)
{
int start = ((int *)arg)[0];
int end = ((int *)arg)[1];
while (start <= end) {
sum += start;
++start;
}
}
int main(int argc, char * argv[])
{
pthread_t id_t1, id_t2;
int range1[] = {1, 5};
int range2[] = {6, 10};
pthread_create(&id_t1, NULL, thread_summation, (void *)range1);
pthread_create(&id_t2, NULL, thread_summation, (void *)range2);
pthread_join(id_t1, NULL);
pthread_join(id_t2, NULL);
printf("result: %d\n", sum);
return 0;
}- 编译运行
1
2
3shiqi@pc:~/network/ch18$ gcc thread3.c -D_REENTRANT -lpthread -o tr3
shiqi@pc:~/network/ch18$ ./tr3
result: 55
- 编译运行
- c18p293:工作线程模型示例程序 thread4.c,验证临界区错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
long long num = 0;
void * thread_inc(void * arg)
{
int i;
for (i = 0; i < 50000000; ++i) {
++num;
}
return NULL;
}
void * thread_des(void * arg)
{
int i;
for (i = 0; i < 50000000; ++i) {
--num;
}
return NULL;
}
int main(int argc, char * argv[])
{
pthread_t thread_id[NUM_THREAD];
int i;
printf("sizeof long long: %ld\n", sizeof(long long));
for (i = 0; i < NUM_THREAD; ++i) {
if (i % 2) {
pthread_create(&(thread_id[i]), NULL, thread_inc, NULL);
} else {
pthread_create(&(thread_id[i]), NULL, thread_des, NULL);
}
}
for (i = 0; i < NUM_THREAD; ++i) {
pthread_join(thread_id[i], NULL);
}
printf("result: %lld\n", num);
return 0;
}- 上述示例中共创建了100个线程,其中一半执行thread_ inc函数中的代码,另一半则执行thread_des函数中的代码。全局变量num经过增减过程后应等于0,通过运行结果观察是否真能得到
- 编译运行
1
2
3
4
5
6
7shiqi@pc:~/network/ch18$ gcc thread4.c -D_REENTRANT -lpthread -o tr4
shiqi@pc:~/network/ch18$ ./tr4
sizeof long long: 8
result: 11790109
shiqi@pc:~/network/ch18$ ./tr4
sizeof long long: 8
result: 804248
18.4 线程同步
- c18p300:互斥量的创建和销毁函数原型 pthread_mutex_init 和 pthread_mutex_destroy
1
2
3
4
5
6
7
int pthread_mutex_init(pthread_mutex_t * mutex,
const pthread_mutexattr_t * attr);
int pthread_mutex_destroy(pthread_mutex_t * mutex);
// mutex:创建/销毁互斥量时传递互斥量的变量地址值
// attr:传递即将创建的互斥量属性,没有特别需要指定的属性时传递 NULL
// 成功时返回 0,失败时返回其他值- 使用前先声明:
pthread_mutex_t mutex;
- 如果不需要配置特殊的互斥量属性,则向第二个参数传递 NULL 时,可利用 PTHREAD_MUTEX_INITIALIZER 宏声明:
pthread_mutex_t mutex mutex = PTHREAD_MUTEX_INITIALIZER;
- 使用前先声明:
- c18p301:互斥量的锁住和释放函数原型 pthread_mutex_lock 和 pthread_mutex_unlock
1
2
3
4
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 成功时返回 0,失败时返回其他值 - c18p302:互斥量使用的示例程序 mutex.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
long long num = 0;
pthread_mutex_t mutex;
void * thread_inc(void * arg)
{
int i;
pthread_mutex_lock(&mutex);
for (i = 0; i < 50000000; ++i) {
++num;
}
pthread_mutex_unlock(&mutex);
return NULL;
}
void * thread_des(void * arg)
{
int i;
pthread_mutex_lock(&mutex);
for (i = 0; i < 50000000; ++i) {
--num;
}
pthread_mutex_unlock(&mutex);
return NULL;
}
int main(int argc, char * argv[])
{
pthread_t thread_id[NUM_THREAD];
int i;
pthread_mutex_init(&mutex, NULL);
for (i = 0; i < NUM_THREAD; ++i) {
if (i % 2) {
pthread_create(&(thread_id[i]), NULL, thread_inc, NULL);
} else {
pthread_create(&(thread_id[i]), NULL, thread_des, NULL);
}
}
for (i = 0; i < NUM_THREAD; ++i) {
pthread_join(thread_id[i], NULL);
}
printf("result: %lld\n", num);
pthread_mutex_destroy(&mutex);
return 0;
}- 编译运行
1
2
3shiqi@pc:~/network/ch18$ gcc mutex.c -D_REENTRANT -lpthread -o mutex
shiqi@pc:~/network/ch18$ ./mutex
result: 0
- 编译运行
- c18p304:信号量的创建和销毁函数原型 sem_init 和 sem_destroy
1
2
3
4
5
6
7
8
9
10
int sem_init(sem_t * sem, int pshared,
unsigned int value);
int sem_destroy(sem_t * sem);
// sem: 创建信号量时传递保存信号量的变量地址值,
// 销毁时传递需要销毁的信号量的变量地址值
// pshared: 传递其它值时,创建可由多个进程共享的信号量
// 传递 0 时,创建只允许 1 个进程内部使用的信号量
// value: 指定新创建的信号量初始值
// 成功时返回 0,失败时返回其他值 - c18p304:信号量使用的函数原型 sem_post 和 sem_wait
1
2
3
4
5
6
7
int sem_post(sem_t * sem);
int sem_wait(sem_t * sem);
// sem: 传递保存信号量读取值的变量地址值,
// 传递给 sem_post 时信号量增 1,
// 传递给 sem_wait 时信号量减 1
// 成功时返回 0,失败时返回其他值- 信号量的值不能小于 0,因此在信号量为 0 的情况下调用 sem_wait 函数时,调用函数的线程将进入阻塞状态。只有当有其它线程调用 sem_post 函数将信号量变为 1 时,原本阻塞的线程可以将该信号量重新减为 0 并跳出阻塞状态
- c18p305:信号量使用示例 semaphore.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
static sem_t sem_one;
static sem_t sem_two;
static int num;
void * read(void * arg)
{
int i;
for (i = 0; i < 5; ++i) {
fputs("Input num: ", stdout);
sem_wait(&sem_two);
scanf("%d", &num);
sem_post(&sem_one);
}
return NULL;
}
void * accu(void * arg)
{
int sum = 0;
int i;
for (i = 0; i < 5; ++i) {
sem_wait(&sem_one);
sum += num;
sem_post(&sem_two);
}
printf("Result: %d \n", sum);
return NULL;
}
int main(int argc, char * argv[])
{
pthread_t id_t1, id_t2;
sem_init(&sem_one, 0, 0);
sem_init(&sem_two, 0, 1);
pthread_create(&id_t1, NULL, read, NULL);
pthread_create(&id_t2, NULL, accu, NULL);
pthread_join(id_t1, NULL);
pthread_join(id_t2, NULL);
sem_destroy(&sem_one);
sem_destroy(&sem_two);
return 0;
}- 编译运行
1
2
3
4
5
6
7
8shiqi@pc:~/network/ch18$ gcc semaphore.c -D_REENTRANT -lpthread -o sema
shiqi@pc:~/network/ch18$ ./sema
Input num: 1
Input num: 2
Input num: 3
Input num: 4
Input num: 5
Result: 15
- 编译运行
18.5 线程的销毁和多线程并发服务器端的实现
- c18p307:销毁线程的 3 种方法
- 调用 pthread_join
- 调用pthread_join函数时,不仅会等待线程终止,还会引导线程销毁。但该函数的问题是,线程终止前,调用该函数的线程将进人阻塞状态
- 调用 pthread_detach
- pthread_detach 不会引起线程终止或进入阻塞状态,可以通过该函数引导销毁线程创建的内存空间
- 调用 pthread_join
- c18p307:pthread_detach 函数原型
1
2
3
4
int pthread_detach(pthread_t thread);
// thread: 终止时需要销毁的线程 ID
// 成功时返回 0,失败时返回其他值 - c18p307:多线程并发聊天服务器的实现 chat_server.c,chat_clnt.c
- 服务端 chat_server.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
int clnt_cnt = 0;
int clnt_socks[MAX_CLNT];
pthread_mutex_t mutx;
void error_handling(char *msg)
{
fputs(msg, stderr);
fputc('\n', stderr);
exit(1);
}
void send_msg(char * msg, int len)
{
int i;
pthread_mutex_lock(&mutx);
for (i = 0; i < clnt_cnt; ++i) {
write(clnt_socks[i], msg, len);
}
pthread_mutex_unlock(&mutx);
}
void * handle_clnt(void * arg)
{
int clnt_sock = *((int *)arg);
int str_len = 0;
int i;
char msg[BUF_SIZE];
while ((str_len = read(clnt_sock, msg, sizeof(msg))) != 0) {
send_msg(msg, str_len);
}
pthread_mutex_lock(&mutx);
for (i = 0; i < clnt_cnt; ++i) {
if (clnt_sock == clnt_socks[i]) {
while (i < clnt_cnt - 1) {
i += 1;
clnt_socks[i] = clnt_socks[i+1];
}
break;
}
}
--clnt_cnt;
pthread_mutex_unlock(&mutx);
close(clnt_sock);
return NULL;
}
int main(int argc, char * argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
int clnt_adr_sz;
pthread_t t_id;
if (argc != 2) {
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}
pthread_mutex_init(&mutx, NULL);
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if (bind(serv_sock,
(struct sockaddr *)&serv_adr,
sizeof(serv_adr)) == -1) {
error_handling("bind() error");
}
if (listen(serv_sock, 5) == -1) {
error_handling("listen() error");
}
while (1) {
clnt_adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock,
(struct sockaddr *)&clnt_adr,
&clnt_adr_sz);
pthread_mutex_lock(&mutx);
clnt_socks[clnt_cnt++] = clnt_sock;
pthread_mutex_unlock(&mutx);
pthread_create(&t_id, NULL, handle_clnt, (void *)&clnt_sock);
pthread_detach(t_id);
printf("Connceted client IP: %s\n", inet_ntoa(clnt_adr.sin_addr));
}
close(serv_sock);
return 0;
} - 客户端 chat_clnt.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
char name[NAME_SIZE] = "[DEFAULT]";
char msg[BUF_SIZE];
void error_handling(char *msg)
{
fputs(msg, stderr);
fputc('\n', stderr);
exit(1);
}
void * send_msg(void * arg)
{
int sock = *((int *)arg);
char name_msg[NAME_SIZE+BUF_SIZE];
while (1) {
fgets(msg, BUF_SIZE, stdin);
if (!strcmp(msg, "q\n") || !strcmp(msg, "Q\n")) {
close(sock);
exit(0);
}
sprintf(name_msg, "%s %s", name, msg);
write(sock, name_msg, strlen(name_msg));
}
return NULL;
}
void * recv_msg(void * arg) {
int sock = *((int *)arg);
char name_msg[NAME_SIZE+BUF_SIZE];
int str_len;
while (1) {
str_len = read(sock, name_msg, NAME_SIZE+BUF_SIZE-1);
if (str_len == -1) {
return (void *)-1;
}
name_msg[str_len] = 0;
fputs(name_msg, stdout);
}
return NULL;
}
int main(int argc, char * argv[])
{
int sock;
struct sockaddr_in serv_addr;
pthread_t snd_thread, rcv_thread;
void * thread_return;
if (argc != 4) {
printf("Usage: %s <IP> <port> <name>\n", argv[0]);
exit(1);
}
sprintf(name, "[%s]", argv[3]);
sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
if (connect(sock,
(struct sockaddr *)&serv_addr,
sizeof(serv_addr)) == -1) {
error_handling("connect() error");
}
pthread_create(&snd_thread, NULL, send_msg, (void *)&sock);
pthread_create(&rcv_thread, NULL, recv_msg, (void *)&sock);
pthread_join(snd_thread, &thread_return);
pthread_join(rcv_thread, &thread_return);
close(sock);
return 0;
} - 编译运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24shiqi@pc:~/network/ch18$ gcc chat_server.c -D_REENTRANT -lpthread -o cserv
shiqi@pc:~/network/ch18$ gcc chat_clnt.c -D_REENTRANT -lpthread -o cclnt
shiqi@pc:~/network/ch18$ ./cserv 9190
Connceted client IP: 127.0.0.1
Connceted client IP: 127.0.0.1
Connceted client IP: 127.0.0.1
shiqi@pc:~/network/ch18$ ./cclnt 127.0.0.1 9190 zhangsan
hi boys
[zhangsan] hi boys
[lisi] hi I'm lisi
[wangwu] well, who am i?
shiqi@pc:~/network/ch18$ ./cclnt 127.0.0.1 9190 lisi
[zhangsan] hi boys
hi I'm lisi
[lisi] hi I'm lisi
[wangwu] well, who am i?
shiqi@pc:~/network/ch18$ ./cclnt 127.0.0.1 9190 wangwu
[zhangsan] hi boys
[lisi] hi I'm lisi
well, who am i?
[wangwu] well, who am i?
- 服务端 chat_server.c
第 24 章 制作HTTP服务器端
24.2 实现简单的Web服务器端
- c24p298:实现基于 Linux 的多线程 Web 服务器端的程序示例 webserv_linux.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
void send_error(FILE * fp)
{
char protocol[] = "HTTP/1.0 400 Bad Request\r\n";
char server[] = "Server:Linux Web Server \r\n";
char cnt_len[] = "Content-length:2048\r\n";
char cnt_type[] = "Content-type:text/html\r\n\r\n";
char content[] = "<html><head><title>NETWORK</title></head>"
"<body><font size=+5><br> 发生错误!查看请求文件名和请求方式!"
"</font></body></html>";
fputs(protocol, fp);
fputs(server, fp);
fputs(cnt_len, fp);
fputs(cnt_type, fp);
fputs(content, fp);
fflush(fp);
}
void send_data(FILE * fp, char * ct, char * file_name)
{
char protocol[] = "HTTP/1.0 200 OK\r\n";
char server[] = "Server:Linux Web Server \r\n";
char cnt_len[] = "Content-length:2048\r\n";
char cnt_type[SMALL_BUF];
char buf[BUF_SIZE];
FILE * send_file;
sprintf(cnt_type, "Content-type:%s\r\n\r\n", ct);
send_file = fopen(file_name, "r");
if (send_file == NULL) {
send_error(fp);
return;
}
// 传输头信息
fputs(protocol, fp);
fputs(server, fp);
fputs(cnt_len, fp);
fputs(cnt_type, fp);
// 传输请求数据
while (fgets(buf, BUF_SIZE, send_file) != NULL) {
fputs(buf, fp);
fflush(fp);
}
fflush(fp);
fclose(fp);
}
char * content_type(char * file)
{
char extension[SMALL_BUF];
char file_name[SMALL_BUF];
strcpy(file_name, file);
strtok(file_name, ".");
strcpy(extension, strtok(NULL, "."));
if (!strcmp(extension, "html") || !strcmp(extension, "htm")) {
return "text/html";
} else {
return "text/plain";
}
}
void error_handling(char * message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
void * request_handler(void * arg)
{
int clnt_sock = *((int *)arg);
char req_line[SMALL_BUF];
FILE * clnt_read;
FILE * clnt_write;
char method[10];
char ct[15];
char file_name[30];
clnt_read = fdopen(clnt_sock, "r");
clnt_write = fdopen(dup(clnt_sock), "w");
fgets(req_line, SMALL_BUF, clnt_read);
if (strstr(req_line, "HTTP/") == NULL) {
send_error(clnt_write);
fclose(clnt_read);
fclose(clnt_write);
return NULL;
}
strcpy(method, strtok(req_line, " /"));
strcpy(file_name, strtok(NULL, " /"));
strcpy(ct, content_type(file_name));
if (strcmp(method, "GET") != 0) {
send_error(clnt_write);
fclose(clnt_read);
fclose(clnt_write);
return NULL;
}
fclose(clnt_read);
send_data(clnt_write, ct, file_name);
}
int main(int argc, char * argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
int clnt_adr_size;
char buf[BUF_SIZE];
pthread_t t_id;
if (argc != 2) {
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if (bind(serv_sock,
(struct sockaddr *)&serv_adr,
sizeof(serv_adr)) == -1) {
error_handling("bind() error");
}
if (listen(serv_sock, 20) == -1) {
error_handling("listen() error");
}
while (1) {
clnt_adr_size = sizeof(clnt_adr);
clnt_sock = accept(serv_sock,
(struct sockaddr *)&clnt_adr,
&clnt_adr_size);
printf("Connection Request: %s:%d\n",
inet_ntoa(clnt_adr.sin_addr), ntohs(clnt_adr.sin_port));
pthread_create(&t_id, NULL, request_handler, &clnt_sock);
pthread_detach(t_id);
}
close(serv_sock);
return 0;
}- 写完才知道写一个完备的 http 服务器其实不容易
- 编译运行,同时要在这建一个 index.html 文件,假设里面都是「hhhhhhhhhhhhhhh」
1
2
3
4shiqi@pc:~/network/ch24$ gcc webserv_linux.c -D_REENTRANT -lpthread -o webs
shiqi@pc:~/network/ch24$ ./webs 9190
Connection Request: 127.0.0.1:53270
Connection Request: 127.0.0.1:532721
2
3shiqi@pc:~/network/ch24$ curl localhost:9190/index.html
hhhhhhhhhhhhhhhhhhhh
curl: (18) transfer closed with 2027 bytes remaining to read