博客
关于我
Linux C网络编程内容总结
阅读量:254 次
发布时间:2019-03-01

本文共 6290 字,大约阅读时间需要 20 分钟。

总的来说 Linux C 网络编程是一块比较难的内容,且有一些枯燥, 

基础内容涉及Linux基础,Linux系统编程和Linux网络编程三部分内容。

Linux基础包括最简单的基本操作,vim的使用,值得注意的是,在VS2017以上版本,可以直接用VS跨平台编写Linux程序,这样可以提高开发效率。

Linux系统编程主要涉及一些Linux系统调用API的使用,特别是进程和线程,进程间通信,与线程同步,这一块内容是最重要的。为后期高并发Web服务器编程作铺垫。

Linux网络编程的基础内容就是TCP/IP SOCKET 编程,基本内容就是《TCP/IP网络编程》中的所有内容。

深入内容有高性能服务器的实现,以及开源库的使用,以及源码的阅读,这一部分难度较大,属于项目部分的内容。

高性能服务器首先要解决的是IO问题

 

Reactor的概念特别重要

Proactor模式

这一部分涉及高并发服务器底层实现问题中,难度比较大

最后,总结分析一下,一些开源库的使用和源码的阅读

webbench的原理是:

webbench首先fork出多个子进程,每个子进程都循环做web访问测试。子进程把访问的结果通过pipe告诉父进程,父进程做最终的统计结果。

webbench需要统计的量是查询率, 每秒响应的请求数,这个量衡量了服务器的并发能力。

上,经常用每秒查询率来衡量服务器的机器的性能,其即为QPS。

对应fetches/sec,即每秒的响应请求数,也即是最大吞吐能力。所以webbench的核心逻辑是下面这个图

 

具体的子进程进行单元测试的时候,逻辑是下面这样的

因此这个程序就存在一些细节,就是模拟发送一个HTTP请求头的部分,因为这是一个命令行工具程序,还涉及到终端输入参数的解析。所以这个程序就比较复杂。

 

tinyHttpd是一个简易的Web服务器,在TCP传输层中,直接启动一个线程接收TCP连接

直接看accept_request函数

void accept_request(int client){    char buf[1024];    int numchars;    char method[255];    char url[255];    char path[512];    size_t i, j;    struct stat st;    int cgi = 0;      /* becomes true if server decides this is a CGI program */    char *query_string = NULL;    /*得到请求的第一行*/    numchars = get_line(client, buf, sizeof(buf));    i = 0; j = 0;    /*把客户端的请求方法存到 method 数组*/    while (!ISspace(buf[j]) && (i < sizeof(method) - 1))    {        method[i] = buf[j];        i++; j++;    }    method[i] = '\0';    /*如果既不是 GET 又不是 POST 则无法处理 */    if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))    {        unimplemented(client);        return;    }    /* POST 的时候开启 cgi */    if (strcasecmp(method, "POST") == 0)        cgi = 1;    /*读取 url 地址*/    i = 0;    while (ISspace(buf[j]) && (j < sizeof(buf)))        j++;    while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))    {        /*存下 url */        url[i] = buf[j];        i++; j++;    }    url[i] = '\0';    /*处理 GET 方法*/    if (strcasecmp(method, "GET") == 0)    {        /* 待处理请求为 url */        query_string = url;        while ((*query_string != '?') && (*query_string != '\0'))            query_string++;        /* GET 方法特点,? 后面为参数*/        if (*query_string == '?')        {            /*开启 cgi */            cgi = 1;            *query_string = '\0';            query_string++;        }    }    /*格式化 url 到 path 数组,html 文件都在 htdocs 中*/    sprintf(path, "htdocs%s", url);    /*默认情况为 index.html */    if (path[strlen(path) - 1] == '/')        strcat(path, "index.html");    /*根据路径找到对应文件 */    if (stat(path, &st) == -1) {        /*把所有 headers 的信息都丢弃*/        while ((numchars > 0) && strcmp("\n", buf))  /* read & discard headers */            numchars = get_line(client, buf, sizeof(buf));        /*回应客户端找不到*/        not_found(client);    }    else    {        /*如果是个目录,则默认使用该目录下 index.html 文件*/        if ((st.st_mode & S_IFMT) == S_IFDIR)            strcat(path, "/index.html");      if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)    )          cgi = 1;      /*不是 cgi,直接把服务器文件返回,否则执行 cgi */      if (!cgi)          serve_file(client, path);      else          execute_cgi(client, path, method, query_string);    }    /*断开与客户端的连接(HTTP 特点:无连接)*/    close(client);}

和处理cgi函数

void execute_cgi(int client, const char *path, const char *method, const char *query_string){    char buf[1024];    int cgi_output[2];    int cgi_input[2];    pid_t pid;    int status;    int i;    char c;    int numchars = 1;    int content_length = -1;    buf[0] = 'A'; buf[1] = '\0';    if (strcasecmp(method, "GET") == 0)        /*把所有的 HTTP header 读取并丢弃*/        while ((numchars > 0) && strcmp("\n", buf))  /* read & discard headers */            numchars = get_line(client, buf, sizeof(buf));    else    /* POST */    {        /* 对 POST 的 HTTP 请求中找出 content_length */        numchars = get_line(client, buf, sizeof(buf));        while ((numchars > 0) && strcmp("\n", buf))        {            /*利用 \0 进行分隔 */            buf[15] = '\0';            /* HTTP 请求的特点*/            if (strcasecmp(buf, "Content-Length:") == 0)                content_length = atoi(&(buf[16]));            numchars = get_line(client, buf, sizeof(buf));        }        /*没有找到 content_length */        if (content_length == -1) {            /*错误请求*/            bad_request(client);            return;        }    }    /* 正确,HTTP 状态码 200 */    sprintf(buf, "HTTP/1.0 200 OK\r\n");    send(client, buf, strlen(buf), 0);    /* 建立管道*/    if (pipe(cgi_output) < 0) {        /*错误处理*/        cannot_execute(client);        return;    }    /*建立管道*/    if (pipe(cgi_input) < 0) {        /*错误处理*/        cannot_execute(client);        return;    }    if ((pid = fork()) < 0 ) {        /*错误处理*/        cannot_execute(client);        return;    }    if (pid == 0)  /* child: CGI script */    {        char meth_env[255];        char query_env[255];        char length_env[255];        /* 把 STDOUT 重定向到 cgi_output 的写入端 */        dup2(cgi_output[1], 1);        /* 把 STDIN 重定向到 cgi_input 的读取端 */        dup2(cgi_input[0], 0);        /* 关闭 cgi_input 的写入端 和 cgi_output 的读取端 */        close(cgi_output[0]);        close(cgi_input[1]);        /*设置 request_method 的环境变量*/        sprintf(meth_env, "REQUEST_METHOD=%s", method);        putenv(meth_env);        if (strcasecmp(method, "GET") == 0) {            /*设置 query_string 的环境变量*/            sprintf(query_env, "QUERY_STRING=%s", query_string);            putenv(query_env);        }        else {   /* POST */            /*设置 content_length 的环境变量*/            sprintf(length_env, "CONTENT_LENGTH=%d", content_length);            putenv(length_env);        }        /*用 execl 运行 cgi 程序*/        execl(path, path, NULL);        exit(0);    } else {    /* parent */        /* 关闭 cgi_input 的读取端 和 cgi_output 的写入端 */        close(cgi_output[1]);        close(cgi_input[0]);        if (strcasecmp(method, "POST") == 0)            /*接收 POST 过来的数据*/            for (i = 0; i < content_length; i++) {                recv(client, &c, 1, 0);                /*把 POST 数据写入 cgi_input,现在重定向到 STDIN */                write(cgi_input[1], &c, 1);            }        /*读取 cgi_output 的管道输出到客户端,该管道输入是 STDOUT */        while (read(cgi_output[0], &c, 1) > 0)            send(client, &c, 1, 0);        /*关闭管道*/        close(cgi_output[0]);        close(cgi_input[1]);        /*等待子进程*/        waitpid(pid, &status, 0);    }}

基于xinetd库可以实现一个简单的xhttp服务器程序

å¨è¿éæå¥å¾çæè¿°

当然还有更复杂的libevent等库的使用。

你可能感兴趣的文章
mysql 记录的增删改查
查看>>
MySQL 设置数据库的隔离级别
查看>>
MySQL 证明为什么用limit时,offset很大会影响性能
查看>>
Mysql 语句操作索引SQL语句
查看>>
MySQL 误操作后数据恢复(update,delete忘加where条件)
查看>>
MySQL 调优/优化的 101 个建议!
查看>>
mysql 转义字符用法_MySql 转义字符的使用说明
查看>>
mysql 输入密码秒退
查看>>
mysql 递归查找父节点_MySQL递归查询树状表的子节点、父节点具体实现
查看>>
mysql 通过查看mysql 配置参数、状态来优化你的mysql
查看>>
mysql 里对root及普通用户赋权及更改密码的一些命令
查看>>
Mysql 重置自增列的开始序号
查看>>
mysql 锁机制 mvcc_Mysql性能优化-事务、锁和MVCC
查看>>
MySQL 错误
查看>>
mysql 随机数 rand使用
查看>>
MySQL 面试题汇总
查看>>
MySQL 面试,必须掌握的 8 大核心点
查看>>
MySQL 高可用性之keepalived+mysql双主
查看>>
MySQL 高性能优化规范建议
查看>>
mysql 默认事务隔离级别下锁分析
查看>>