原理基础 用 PHP 来实现一个动态 Web 服务器 我在这篇博客里面写得很清楚了
unix socket 基础 简单的 Socket 通信
其实呢这次就是把原来 php 封装好的代码,再还原回 c 代码。
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #define SERV_PORT 8031 int main(void) { int lfd, cfd; struct sockaddr_in serv_addr,clin_addr; socklen_t clin_len; char buf[1024]; int len; lfd = socket(AF_INET,SOCK_STREAM,0); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(SERV_PORT); bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); listen(lfd, 128); while(1){ clin_len = sizeof(clin_addr); cfd = accept(lfd, (struct sockaddr *)&clin_addr, &clin_len); len = read(cfd,buf,sizeof(buf)); write(STDOUT_FILENO,buf,len); char web_result[] = "hello world\n"; write(cfd,web_result,sizeof(web_result)); close(cfd); } close(lfd); return 0; }
当我在浏览器中访问http://10.211.55.4:8031/
的时候,服务器端会输出本次的访问的信息:
GET / HTTP/1.1 Host: 10.211.55.4:8031 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36 Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,ja;q=0.2
OK,运行正常,访问之后会提示下载,下载的文件打开里面的内容即为hello world
。
下面就是实现http
协议,以符合http
协议的内容返回,浏览器才能做出相应的解析。这在我之前的 php web 服务器里面也写过,这就是修改下语言咯。
没想到遇到一点问题,卡了好久。
就是计算文本输出长度的时候,我想输出
hello world
,写的文本长度是12,因为计算了char
的最后一位\0
,实际输出到浏览器只用输出hello world
该字符串的实际长度,本身是11,而我写的12,那么浏览器一直觉得文本没接受完毕,一直处于阻塞状态。还是要感谢网友
zonxin
的悉心解答 http://segmentfault.com/q/1010000003986172
原理模型图
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #define SERV_PORT 8031 int main(void) { int lfd, cfd; struct sockaddr_in serv_addr,clin_addr; socklen_t clin_len; char buf[1024]; int len; if((lfd = socket(AF_INET,SOCK_STREAM,0)) == -1){ perror("create socket failed"); exit(1); } memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(SERV_PORT); if(bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) { perror("bind error"); exit(1); } if(listen(lfd, 128) == -1) { perror("listen error"); exit(1); } while(1) { clin_len = sizeof(clin_addr); cfd = accept(lfd, (struct sockaddr *)&clin_addr, &clin_len); //len = read(cfd,buf,sizeof(buf)); //write(STDOUT_FILENO,buf,len); char web_result[] = "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\nContent-Length: 11\r\nServer: mengkang\r\n\r\nhello world"; write(cfd,web_result,sizeof(web_result)); close(cfd); } close(lfd); return 0; }
我又做了下小实验,当我把长度写成12时,同时在写入客户端 fd 的前后分别打印一次,就能输出到客户端,而且在输出的内容后面跟过去一个小红点。
char web_result[] = "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\nContent-Length: 12\r\nServer: mengkang\r\n\r\nhello world"; printf("\n"); write(cfd,web_result,sizeof(web_result)); printf("\n");
姑且认为服务器先给浏览器发过去,因为连接没有关闭,所以浏览器认为这只是一部分,所以就先渲染了,然后服务器关闭连接。结果浏览器发现被坑了,于是就告诉用户这里(小红点的地方)还应该有内容但是服务器没返回回来。
好,结束上面这个输出小红点的问题,对上面正常输出的静态 web 服务器进行 ab 压测,截取部分结果:
[root@localhost ~]# ab -n30000 -c3000 http://127.0.0.1:8031/ Server Software: mengkang Server Hostname: 127.0.0.1 Server Port: 8031 Document Path: / Document Length: 12 bytes Concurrency Level: 3000 Time taken for tests: 1.635 seconds Complete requests: 30000 Failed requests: 0 Write errors: 0 Total transferred: 2792697 bytes HTML transferred: 360348 bytes Requests per second: 18349.03 [#/sec] (mean) Time per request: 163.496 [ms] (mean)