tinyhttpd是一个十分简单的http多线程服务器,通过详细分析500行左右的代码,对http请求和响应有一个简单的理解。
http://sourceforge.net/projects/tinyhttpd/
首先,从main函数开始:
<pre name="code" class="cpp">int main(void)
{
int server_sock = -1;
u_short port = 0;
int client_sock = -1;
struct sockaddr_in client_name;
int 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, accept_request, client_sock) != 0)
perror("pthread_create");
}
close(server_sock);
return(0);
}
3-8行:变量初始化工作
10行:startup函数,
int startup(u_short *port)
{
int httpd = 0;
struct sockaddr_in name;
httpd = socket(PF_INET, SOCK_STREAM, 0);
if (httpd == -1)
error_die("socket");
memset(&name, 0, sizeof(name));
name.sin_family = AF_INET;
name.sin_port = htons(*port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
error_die("bind");
if (*port == 0) /* if dynamically allocating a port */
{
int namelen = sizeof(name);
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
error_die("getsockname");
*port = ntohs(name.sin_port);
}
if (listen(httpd, 5) < 0)
error_die("listen");
return(httpd);
}
传入参数:*port,由于main函数中port为0,未指定绑定端口号,需要系统分配。
4行:struct socketaddr_in,是一个表示使用地址族、端口和网络地址的结构体。
struct sockaddr_in
{
short sin_family;/*Addressfamily一般来说AF_INET(地址族)PF_INET(协议族)*/
unsigned short sin_port;/*Portnumber(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/
struct in_addr sin_addr;/*Internetaddress*/
unsigned char sin_zero[8];/*Samesizeasstructsockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/
};
6行:初始化服务器socket。使用Internet协议族,protocol family Internet;SOCK_STREAM对应TCP协议,SOCK_DGRAM对应UDP协议,SOCK_RAW是面向底层协议的。
11行:htons(),将主机字节顺序转换为网络字节顺序。h,host;n,network;s,short(2Bytes);l:long(4Bytes)。另外还有htonl(),ntohs(),ntohl()函数。
12行:INADDR_ANY:0.0.0.0,表示所有地址,绑定监听本机的所有IP地址。
13行:进行绑定,httpd与name对应的地址端口绑定。
15-21行:由于*port=0,系统会自动分配一个端口,使用getsockname获取。注意:必须在bind成功后才能获取。
22行:开始监听,后一个参数5表示决定了未完成队列和已完成队列中连接数目之和的最大值。