main函数
tinyhttp main函数源码如下:
int main(void)
{
int server_sock = -1;
u_short port = 0;
int client_sock = -1;
struct sockaddr_in client_name;
/*
源于sockaddr
sockaddr_in 在netinet/in.h中定义
struct sockaddr_in
{
short sin_family; //Address family一般来说AF_INET(地址族)PF_INET(协议族)
unsigned short sin_port;//Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成
网络数据格式的数字)
struct in_addr sin_addr;// in_addr是一种结构体,可以用来表示一个32位的IPv4地址,位于
arpa/inet.h头文件。IP address in network byte order(Internet address)
unsigned char sin_zero[8];//Same size as struct sockaddr没有实际意义,只是为了跟sockaddr结
构在内存中对齐
};
*/
socklen_t client_name_len = sizeof(client_name);
pthread_t newthread;
server_sock = startup(&port); //http服务建立在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,(void *) &client_sock) != 0) //线程创建函数
perror("pthread_create");
}
close(server_sock);
return(0);
}
主要分为以下几步:
- 数据初始化;
- startup函数,传入端口参数,如果port为0动态分配一个端口;
- 建立一个无限循环等待请求,当请求到来时,建立一个线程处理它;
- 关闭。
startup函数解析
startup函数实现socket、bind以及listen。具体流程:
- 套接字描述符初始化、sockaddr_in声明;
- 建立socket;
socket原型:
#include<sys/socket.h>
int socket(int family, int type, int protocol);
返回: 成功犯规非负描述符;出错返回-1
/*其中family指明协议族,tinyhttp指定为PF_INET,即IPv4协议;
type指明套接字类型,tinyhttp指定为SOCK_STREAM,即字节流套接字;
protocol指定传输协议,设为0时,系统会根据family和type组合选择系统默认值;*/
- 初始化sockadrr_in;
- 通过bind把本地协议地址赋予一个套接字,bind可以指定IP地址或端;
bind原型:
#include<sys/socket/h>
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
返回: 成功为0,失败为-1.
- 如果端口为0,则动态分配一个端口,调用getsockname;
getsockname原型:
#include<sys/socket.h>
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
返回: 成功返回0, 失败返回-1
/*使用该函数有两种原因:1. 在一个没有调用bind的TCP客户端,connect成功返回后,getsockname用于返回
由内核赋予该链接的本地IP地址和本地端口号;2. 在以端口号0调用bind(告知内核去选择本地端口号)后,
getsockname用于返回由内核赋予的本地端口号。*/
- listen作用:当socket创建套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户端套接字。listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应接受向该套接字的连接请求。第二个参数规定了内核应该为相应套接字排队的最大连接个数。
listen原型:
#include<sys/socket.h>
int listen(int sockfd, int backlog);
返回:若成功返回0, 失败返回-1
- 返回套接字描述符。
startup程序
int startup(u_short *port)
{
int httpd = 0;
struct sockaddr_in name;
httpd = socket(PF_INET, SOCK_STREAM, 0); //建立socket 其原型 socket函数解析 IPPROTO_IP
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) //bind解析
error_die("bind");
if (*port == 0) /* if dynamically allocating a port */
{
socklen_t namelen = sizeof(name);
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
error_die("getsockname");
*port = ntohs(name.sin_port);
}
if (listen(httpd, 5) < 0) //backlog = 5,是4.2BSD支持的最大值
error_die("listen");
return(httpd);
}