《深入理解计算机系统》(第三版)一书中提供了很多代码实例,但我一直没有运行过。现在就以第11章 P662 “echo客户端和服务端的示例”为例运行一下。
本次实验均在 WSL2 的 Ubuntu-22.04 系统上进行。
准备头文件
该书中的许多代码都包含了一个名为csapp.h的头文件。Linux系统本身并不包含它,需要自行准备。
下载源代码
点击此处
进入本书配套的网站,找到You can download a tarfile字段,点击链接下载压缩文件。

配套网站
配置环境
接下来需要将下载的压缩包中csapp.h和csapp.c文件解压出来,放到系统的/usr/include文件夹中。
先进入csapp.h和csapp.c所在的文件夹,然后执行:
1sudo mv csapp.h /usr/include
2sudo mv csapp.c /usr/include
此时,应该能在/usr/include看到这两个文件。
编译并运行
源代码
echoserveri.c文件:
1/*
2 * echoserveri.c - An iterative echo server
3 */
4/* $begin echoserverimain */
5#include "csapp.h"
6
7void echo(int connfd)
8{
9 size_t n;
10 char buf[MAXLINE];
11 rio_t rio;
12
13 Rio_readinitb(&rio, connfd);
14 while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) { //line:netp:echo:eof
15 printf("server received %d bytes\n", n);
16 Rio_writen(connfd, buf, n);
17 }
18}
19
20int main(int argc, char **argv)
21{
22 int listenfd, connfd, port, clientlen;
23 struct sockaddr_in clientaddr;
24 struct hostent *hp;
25 char *haddrp;
26 if (argc != 2) {
27 fprintf(stderr, "usage: %s <port>\n", argv[0]);
28 exit(0);
29 }
30 port = atoi(argv[1]);
31
32 listenfd = Open_listenfd(port);
33 while (1) {
34 clientlen = sizeof(clientaddr);
35 connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
36
37 /* determine the domain name and IP address of the client */
38 hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
39 sizeof(clientaddr.sin_addr.s_addr), AF_INET);
40 haddrp = inet_ntoa(clientaddr.sin_addr);
41 printf("server connected to %s (%s)\n", hp->h_name, haddrp);
42
43 echo(connfd);
44 Close(connfd);
45 }
46 exit(0);
47}
48/* $end echoserverimain */
echoclient.c文件
1/*
2 * echoclient.c - An echo client
3 */
4/* $begin echoclientmain */
5#include "csapp.h"
6
7int main(int argc, char **argv)
8{
9 int clientfd, port;
10 char *host, buf[MAXLINE];
11 rio_t rio;
12
13 if (argc != 3) {
14 fprintf(stderr, "usage: %s <host> <port>\n", argv[0]);
15 exit(0);
16 }
17 host = argv[1];
18 port = atoi(argv[2]);
19
20 clientfd = Open_clientfd(host, port);
21 Rio_readinitb(&rio, clientfd);
22
23 while (Fgets(buf, MAXLINE, stdin) != NULL) {
24 Rio_writen(clientfd, buf, strlen(buf));
25 Rio_readlineb(&rio, buf, MAXLINE);
26 Fputs(buf, stdout);
27 }
28 Close(clientfd); //line:netp:echoclient:close
29 exit(0);
30}
31/* $end echoclientmain */
生成可执行文件
执行命令
1gcc echoserveri.c -o echoserveri
2gcc echoclient.c -o echoclient
得到可执行文件echoserveri和echoclient。
运行服务端和客户端
新建两个终端,分别运行echoserveri和echoclient文件。
服务端终端:
1# 服务端启动时输入端口号
2./echoserveri <port>
客户端终端:
1# 客户端启动时输入主机名和端口号
2./echoclient <host> <port>
3
4# 之后可以输入内容
5# 观察服务端终端的内容
运行结果

