项目简介
此项目可以自行在GitHub上进行下载,作者的ReadMe文档也写得是比较详细的。这里用到了较多的Unix编程,不会的可以一点一点去查。
(实名感谢我们家杰佬发给我的APUE,可以当字典查)
项目内容
其实就是实现了一个轻量级服务器的功能,同时源代码也包含了一个simpleclient的文件,用于生成一个用户端。这和计网课上的用Java写TCP/UDP很像,但C会显得更麻烦一些,但总认为基于Unix给人一种高大上的感觉。
代码分析
我这儿就直接按照作者在README中给出的阅读顺序来浏览了。
由于下边都是我没动过的源码,是存在一些问题的,比如我用Ubuntu21打开文件时就提示我说u_short已经不能用啦,全部改为unsigned short即可哈。
- main函数分析
int main(void)
{
int server_sock = -1;
u_short port = 4000;
int client_sock = -1;
struct sockaddr_in client_name;
socklen_t client_name_len = sizeof(client_name);
pthread_t newthread;
server_sock = startup(&port);
printf("httpd running on port %d\n", port);
while (1)
{
client_sock = accept(server_sock,
(struct sockaddr *)&client_name,
&client_name_len);
if (client_sock == -1)
error_die("accept");
/* accept_request(&client_sock); */
if (pthread_create(&newthread , NULL, (void *)accept_request, (void *)(intptr_t)client_sock) != 0)
perror("pthread_create");
}
close(server_sock);
return(0);
}
这里面直接来定义一些东西实际上是难以理解的,但总体来看还是可以根据英语,结合计算机网络的知识来进行解读。
大家都知道socket是套接字,我们的计算机网络通信机制用的就是socket编程,socket是留给我们开发人员的一个接口。这个接口之强大就在于其帮助我们屏蔽了计算机网络底层的内容,我们无需和那些繁琐的协议(虽然等会一些参数会用到)甚至是那些底层物理结构直接打交道。
先来看变量申明:
int server_sock = -1;
u_short port = 4000;
int client_sock = -1;
struct sockaddr_in client_name;
socklen_t client_name_len = sizeof(client_name);
pthread_t newthread;
server_sock = startup(&port);
printf("httpd running on port %d\n", port);
可以看到我们分别为服务器和客户端都申请了一个套接字并且初始化的时候都为-1,等会我们会用了另外的值替换。
申明端口号4000,这个不难理解。
下述牵扯到地址格式的问题,可以翻阅APUE的16.3.2直接进行学习。
随后我们申明了一个结构体,sockaddr_in,这个可能有必要需查看一下他的定义。
首先官方就交代了,这是一个拿来描述Internet Socket Address的结构体。这就需要发挥计算机网络的知识了,如何来确定这么一个socket的地址呢?
它需要你的IP地址,端口号,协议族,同时此结构体的最后还声明了要针对结构体sockaddr来进行补齐‘0’操作。
那么这俩地址间的关系是什么呢,根据我们的“字典”,sockaddr_in是ipv4域的一个地址结构,sockaddr就是一个统一的可以传入套接字使用的通用地址结构!
所以,综上,应该可以明白为什么有一个padding填充值了吧!
多线程你那个pthread可以先不予理睬,因为作者已经说了这个是需要comment out的!所以我们在Linux环境下make的时候需要先注释掉!
最后就会发现我们调用了一个startup函数用以决定服务器的socket字了,其为int型的主要原因就是unix视万物为文件,这个套接字实际上就是一个文件描述符。
有了套接字就好办了,我们来分析剩下的内容:
while (1)
{
client_sock = accept(server_sock,
(struct sockaddr *)&client_name,
&client_name_len);
if