嗨,老铁,欢迎来到我的博客!

如果觉得我的内容还不错的话,可以关注下我在 segmentfault.com 上的直播。我主要从事 PHP 和 Java 方面的开发,《深入 PHP 内核》作者之一。

[视频直播] PHP 进阶之路 - 亿级 pv 网站架构的技术细节与套路 直播中我将毫无保留的分享我这六年的全部工作经验和踩坑的故事,以及会穿插着一些面试中的 考点难点加分点

周梦康 发表于 2015-11-12 4480 次浏览 标签 : LinuxC

原理基础 用 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

原理模型图

简单静态 web 服务器(循环服务器)的实现

#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)


嗨,老铁,欢迎来到我的博客!

如果觉得我的内容还不错的话,可以关注下我在 segmentfault.com 上的直播。我主要从事 PHP 和 Java 方面的开发,《深入 PHP 内核》作者之一。

[视频直播] PHP 进阶之路 - 亿级 pv 网站架构的技术细节与套路 直播中我将毫无保留的分享我这六年的全部工作经验和踩坑的故事,以及会穿插着一些面试中的 考点难点加分点

评论列表