Tinyhttpd学习
整体工作流程
(1)服务器启动,在指定端口或者随机端口绑定httpd服务
(2)在收到客户端请求后 ,服务器(主线程)accept这个连接,然后创建一个子线程,执行accept_request函数对这个请求进行处理。
(3)通过accept_request函数 提取出 请求的 方法,url 从而判断客户端的请求 ,如果客户端请求的是静态页面,执行 serve_file函数,直接将请求的数据发送给客户端。如果客户端请求的是cgi文件,执行execute_cgi函数,调用cgi文件,服务器将cgi执行得到的结果发送给客户端。
int main(void)
{
int server_sock = -1;
u_short port = 0; //监听端口号
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)//accept失败
error_die("accept");
if (pthread_create(&newthread , NULL, accept_request,(void *)&client_sock) != 0)
perror("pthread_create");
}
return(0);
}
各功能模块详述
startup
初始化httpd服务, 建立套接字、绑定端口、进行监听
socket()----bind()—listen()
int startup(u_short * port )
{
int httpd=0;
struct sockaddr_in name; // 套接字地址 (地址族/端口地址/ip地址)
/*int socket(int domain,int type,int protocol)*/
httpd=socket(PF_INET,SOCK_STREAM,0);
if(http=-1) //初始化套接口失败
error_die("socket");
/*清空套接字地址*/
memset(&name,0,sizeof(name));
name.sin_family=AF_INET;//ipv4
name.sin_port= htons(*port);//大小端字节表示转换
name.sin_addr.s_addr=htonl(INADDR_ANY);//INADDR_ANY表示本机ip
if(bind(httpd,(struct sockaddr *)&name, sizeof(name))<0)// 绑定失败
error_die("bind");
if(*port == 0) /*动态分配一个端口*/
{
int namelen = sizeof(name);
/*在以端口0调用bind 后 ,getsockname 用于返回由内核赋予(动态)的本地端口号*/
if(getsockname(httpd,(struct sockaddr*)&name,&namelen)==-1) //失败
error_die("getsockname");
*port= ntohs(name.sinport);// 网络字节顺序(大端)转换为主机字节顺序
}
if(listen(httpd,5)<0) //服务器监听客户端请求。连接队列的最大连接个数设为5
error_die("listen");
return(httpd);
涉及一些网络编程的函数
1.socket()
初始化接口,返回socket描述符
int socket(int domain,int type,int protocol);
/*
domain用于设置网络通信的域 PF_INET (Ipv4)
type 接口类型
SOCK_STREAM TCP套接口
SOCK_DGRAM UDP套接口
protocol 用于制定某个协议的特定类型
通常设为0
*/
- bind()
绑定套接口,成功返回0失败返回-1
int bind(int sockfd,const struct sockaddr * addr,
socklen_t addrlen);
/*
sockfd: socket()返回的socket描述符
addr:该套接字的地址
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
IP地址和端口一起储存
struct sockaddr_in
{
sa_family_t sin_family; //地址族
uinit16_t sin_port; //16位端口地址
struct in_addr sin_addr;//32位IP地址
char zero[8];//不使用 //与 struct sockaddr一样大
}
struct in_addr
{
in_addr_t s_addr;
}
而在 struct sockaddr_in 结构体中 ip地址和端口号是分开储存。
通常 使用struct sockaddr_in设置后,让后将其强制转换为struct sockaddr类型,然后传递给bind函数即可。
addrlen:所指定的结构体变量的大小
*/
3.listen()
使主动连接套接口变为被连接套接口,listen函数只是将 sockfd 和 backlog 告诉内核, 然后就返回。
成功返回0,失败返回1。
int listen(int sockfd,int backlog);
/*
sockfd&