首先放作者上传项目的地址https://sourceforge.net/projects/tinyhttpd/
本篇博客主要记录一下在学习这个项目时,通过查资料、看博客或看源码掌握的知识。
函数声明
//每次收到一个请求,创建一个线程来处理该请求 //把client_sock转成地址作为参数传入pthread_create void accept_request(void *); //错误请求,HTTP状态码400 void bad_request(int); //读取文件 void cat(int ,FILE*); //得到一行数据,读到\n就认为一行结束;读到\r就用MSG_PEEK的方式读入下一个字符 //若下个字符为'\n'则用recv读,否则将c设为'\n' void get_line(int, char* , int); //开启TCP连接,绑定端口 int startup(ushort *); //如果不是GET或者POST,就报错方法没有实现 void unimplemented(int);
Http请求
GET / HTTP/1.1 Host: 192.168.0.103:5000 Connection: keep-alive
startup()函数
setsockopt()
#include <sys/types.h> #include <sys/socket.h> int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen); /******************************************************************* * 目的:获取或设置与某个套接字关联的选项 * sock:将要被设置或者获取选项的套接字 * level:选项所在的协议层。为了操作套接字层的选项,将其指定为SOL_SOCKET * optname:需要访问的选项名 * optval:对于setsockopt(),指向包含新选项值的缓冲 * optlen:现选项的长度 返回值: * 成功执行返回0,失败返回-1 ********************************************************************/ //项目httpd.c中实际写法,参照理解 if((setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) error_die("setsockopt failed");
bind()给套接字命名
#include <sys/types.h> #include <sys/socket.h> int bind(int sockfd, const struct sockaddr* addr, socklen_t* addrlen); /*********************************************************************** * 当用socket()函数创建套接字后,套接字在网络地址族中存在,但没有任何地址给它 * 赋值。bind()函数把用addr指定的地址赋给用文件描述符代表的套接字sockfd。 * 这个操作一般被称作bind()函数用本地地址给套接字命名。 * 返回值:0,成功;-1,失败。 ************************************************************************/ //项目实际写法 if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0) error_die("bind");
动态分配一个端口
if(*port==0) { socklen_t namelen=sizeof(name); if(getsockname(httpd, (struct sockaddr*) &name, &namelen) == -1) error_die("getsockname"); *port = ntohs(name.sin_port); }
main()
accept函数
接受套接字中已经建立的连接
#include <sys/types.h> #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t addrlen); //accept函数提取所监听套接字的等待队列中第一个连接请求,创建一个新的套接字。新建立的套接字不在监听状态 //原来监听的套接字也不受accept的影响。 //项目实际写法 client_sock = accept(sever_sock, (struct sockaddr *)&client_name, &client_name_len); if(client_sock==-1) error_die("accept");
pthread_create函数
创建线程,也就是确定调用该线程函数的入口点
#include <pthread.h> pthread_t newthread; //pthread_t在linux中被定义为"unsigned long int" int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine) (void*), void *arg) //参数说明 //thread:线程标识符 //attr:线程属性设置 //项目实际写法 if(pthread_create(&newthread, NULL, (void *)accept_request, (void*) (intptr_t)client_sock)!=0) perror("pthread_create");
accept_request函数
处理从套接字上监听到的一个http请求
get_line函数
用处:从缓冲区中读取字符存入字符数组buf中
//中止条件为换行符 //例如读取 GET / HTTP/1.1 int get_line(int sock, char *buf, int size) { int i = 0; char c='\0'; int n; while((i<size-1) && c!='\n') { n = recv(sock, &c, 1, 0); if(n>0) { if(c=='\r') { n = recv(sock, &c, 1, MSG_PEEK); if(n>0 && c=='\n') recv(sock, &c, 1, 0); else c=='\n'; } buf[i]==c; i++; } else c='\n'; } buf[i]='\0'; return i; }
unemplemented函数
用处:通知客户端请求的web方法没有实现
参数:客户端套接字(client socket)
void unemplemented(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 501 Method Not Implemented"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); //#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n" send(client, buf, strlen(buf), 0); }