当前位置: 首页 > Linux

TinyHttp源码分析

时间:2023-04-06 03:50:59 Linux

主要功能1.服务器端初始化:创建socket=>设置端口复用=>绑定socket和服务器地址=>如果没有指定端口则动态分配=>monitorinton=1;unsignedintport=4000;structsockaddr_inname;intlfd=socket(PF_INET,SOCK_STREAM,0);//创建socketmemset(&name,0,sizeof(name));//初始化namename.sin_family=AF_INET;name.sin_port=htons(*port);name.sin_addr.s_addr=htonl(INADDR_ANY);setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))//端口重用绑定(lfd,(structsockaddr*)&name,sizeof(name))if(*port==0){//动态分配端口socklen_tnamelen=sizeof(name);getsockname(lfd,(structsockaddr*)&name,&namelen);*port=ntohs(name.sin_port);}listen(httpd,5);2.服务器阻塞并等待客户端连接。连接后,开启一个线程并执行相应的响应函数:while(1){client_sock=accept(server_sock,(structsockaddr*)&client_name,&client_name_len);printf("在端口%d\n从%s接收",inet_ntop(AF_INET,&client_name.sin_addr,str,sizeof(str)),ntohs(client_name.sin_port));pthread_create(&newthread,NULL,(void*)accept_request,(void*)(intptr_t)client_sock);}响应函数1.获取请求方法:上图为http请求的消息格式,请求方法参考上图中请求行的第一个字段是inti=0;charbuf[1024];charmethod[255];/*get_line返回获取的字节数*/intnumchars=get_line(client_sock,buf,sizeof(buf));/*不为空,未达到上限,将buf中的Get或Post复制到method中*/while(!isspace((int)buf[i])&&(i0)&&strcmp("\n",buf)){/*Content-Length:这个字符串一共15个字符,所以去掉第一句后,设置第16位为结束符,比较一下,第16位就是结束符*/buf[15]='\0';如果(strcasecmp(buf,"Content-Length:")==0)content_length=atoi(&(buf[16]));numchars=get_line(client_sock,buf,sizeof(buf));//剩余的头部内容被读取并丢弃}if(content_length==-1){bad_request(client);return;}3.创建两个管道并分叉:pipe(cgi_output);//输出管道(cgi_input);//输入pid=fork();//创建进程4.子进程:关闭输出读端,输入写端;将输出读取终端复制到标准输出,将输入读取终端复制到标准输入;配置cgi环境变量;execl执行请求地址。charmeth_env[255];charquery_env[255];charlength_env[255];dup2(cgi_output[1],STDOUT);//将读取的输出复制到stdoutdup2(cgi_input[0],STDIN);//将输入读取终端复制到stdinclose(cgi_output[0]);//输出读数close(cgi_input[1]);//输入写入//CGI环境变量sprintf(meth_env,"REQUEST_METHOD=%s",method);putenv(meth_env);if(strcasecmp(method,"GET")==0){//获取请求sprintf(query_env,"QUERY_STRING=%s",query_string);putenv(query_env);}else{//post请求sprintf(length_env,"CONTENT_LENGTH=%d",content_length);putenv(length_env);}execl(path,NULL);exit(0);//子进程退出5.父进程:关闭输出写端,输出读端;如果是post请求,接收post内容,写入输入写入端;循环读取输入读取端的数据,发送给客户端。关闭(cgi_output[1]);//输出写入关闭(cgi_input[0]);//inputreadif(strcasecmp(method,"POST")==0){//通过cgi_input[1](writeterminal)写入CGI的标准输入for(inti=0;i0)send(client_sock,&c,1,0);close(cgi_output[0]);close(cgi_input[1]);waitpid(pid,&status,0);其他重要函数1.get_line:读取一行数据intget_line(intsock,char*buf,intsize){intn;诠释我=0;charc='\0';while((i0){//将\r\n或\r转换为'\n'if(c=='\r'){//在读取'\r'之前读取一个字节n=recv(sock,&c,1,味精聚醚醚酮);//如果读取的是'\n',则读取它,否则c='\n'if((n>0)&&(c=='\n'))recv(sock,&c,1,0);否则c='\n';}//将数据读入bufbuf[i]=c;我++;}否则c='\n';}buf[i]='\0';返回(一);//返回写入buf的字节数}注意index.html一定不能有执行权限,否则无法显示内容,可以改gccserver.c-oserver-lpthreadchmod600index.html编译,请访问完整代码github