自己动手写服务器-1

来源:互联网 发布:jd软件 编辑:程序博客网 时间:2024/05/18 01:06

博客概述

先前关于linux文件系统驱动的博客已经删除,文件系统可能是一个比较复杂的话题,涉及的东西也比较多。我虽然想写,不过发现可能要叙述的东西太多,我没办法掌握重点,所以暂时不写,等以后有好的思路在写。根据朋友的建议,先写服务器开发的博客。首先从简单的回文服务器开始到http服务器。

博客面向的读者是已经熟悉c,unix编程和unix网络编程,然后想编写服务器程序的人。

服务器概述

大部分的服务器都是接受客户端的请求之后做一些处理,然后发回一些内容给客户端。比如回文服务器是接受一段文本,然后回射给客户端。如下给出服务器的一个图例:

一个服务器可能会和很多客户端同时交互,所以保证每个客户端公平的快速响应是很重要的。

mc_echo概述

mc_echo是一个回文服务器,也是本博客讲解的服务器开发的第一个服务器,项目地址为https://github.com/mc-robin/mc_server/tree/master/mc_echo。以下给出mc_echo的大致图例:


主进程用于接受新的客户端连接,然后分发连接请求。处理进程用于处理客户端的请求,然后回应。调度进程用于帮助主进程更加合理的分发服务。mc_echo并不涉及模块机制,所以是比较简单的服务器示例。

mc_echo配置文件解析

mc_echo的配置文件大致如下:

port{ "8888" }user{ "nobody" }group{ "nogroup" }IPversion { "ipv4" }LOGFile{ "/var/log/echo.log" }maxrequests{ "1024" }maxprocesses{ "4096" }upperthreshold{ "512" }lowerthreshold{ "64" }processcreat{ "4" }cycletime{ "60" }backlog{ "1024" }#keyfile{ "/home/mc_server/mc_echo/key.pem" }#certfile { "/home/mc_server/mc_echo/cert.pem" }

port是服务器的监听端口,user和group是服务器运行后的uid和gid(同时接受id和名字格式),ipversion是ip版本,logfile是日志文件路径,maxrequests是处理进程最多能处理的客户端数目,maxprocesses是处理进程的最大数目,upperthreshold和lowerthreshold是调度参数(细节在讲诉调度进程时解析),processcreat是预创建的处理进程数目,cycletime是调度进程的周期,backlog是listen的参数。keyfile和certfile是ssl的加密文件路径。

注: mc_echo只能监听一个端口,mc_httpd能同时监听多个端口(要实现监听多个端口本质不难,细节在讲解mc_httpd时讨论)


mc_echo采用lex和yacc读取配置文件,lex的代码如下:

%{#include        "configure.h"#include        "conf_yacc.h"%}or      [\|,]ws      [ \t\n]+comment #.*string  \"[^\"\n]*[\"\n]%%{ws}            ;{comment}       ;{or}            { return OR; }\{              { return OBRACE; }\}              { return EBRACE; }{string}        { yylval.dstr = strdup(yytext + 1);                  if(yylval.dstr[yyleng - 2] != '"'){                        mc_err("Error: '%s' is Illegal string\n", yytext);                  }else                        yylval.dstr[yyleng - 2] = '\0';                  return STRING;                }[Uu][Ss][Ee][Rr]        { return USER; }[Pp][Oo][Rr][Tt]        { return PORT; }[Gg][Rr][Oo][Uu][Pp]    { return GROUP; }[Cc][Ii][Pp][Hh][Ee][Rr]        { return CIPHER; }[Bb][Aa][Cc][Kk][Ll][Oo][Gg]    { return BACKLOG; }[Ll][Oo][Gg][Ff][Ii][Ll][Ee]    { return LOGFILE; }[Pp][Ii][Dd][Ff][Ii][Ll][Ee]    { return PIDFILE; }[Kk][Ee][Yy][Ff][Ii][Ll][Ee]    { return KEYFILE; }[Cc][Ee][Rr][Tt][Ff][Ii][Ll][Ee]        { return CERTFILE; }[Ii][Pp][Vv][Ee][Rr][Ss][Ii][Oo][Nn]    { return IPVERSION; }[Cc][Yy][Cc][Ll][Ee][Tt][Ii][Mm][Ee]    { return CYCLETIME; }[Mm][Aa][Xx][Rr][Ee][Qq][Uu][Ee][Ss][Tt][Ss]    { return MAXREQUESTS; }[Mm][Aa][Xx][Pp][Rr][Oo][Cc][Ee][Ss][Ss][Ee][Ss]        { return MAXPROCESSES; }[Pp][Rr][Oo][Cc][Ee][Ss][Ss][Cc][Rr][Ee][Aa][Tt]        { return PROCESSCREAT; }[Uu][Pp][Pp][Ee][Rr][Tt][Hh][Rr][Ee][Ss][Hh][Oo][Ll][Dd]        { return UPPERTHRESHOLD; }[Ll][Oo][Ww][Ee][Rr][Tt][Hh][Rr][Ee][Ss][Hh][Oo][Ll][Dd]        { return LOWERTHRESHOLD; }.               ;%%
从代码可以看出,mc_echo的配置文件是不区分大小写的。另外,此处的代码与git上的略有不同,此处的代码更加好。

yacc的代码如下:

%{#undef  YYSTACKSIZE#define YYSTACKSIZE     5000#include        "configure.h"%}%union{        char    *dstr;}%token PORT%token USER%token GROUP%token CIPHER%token BACKLOG%token LOGFILE%token PIDFILE%token KEYFILE%token CERTFILE%token IPVERSION%token CYCLETIME%token MAXREQUESTS%token MAXPROCESSES%token PROCESSCREAT%token UPPERTHRESHOLD%token LOWERTHRESHOLD%token OR%token OBRACE%token EBRACE%token <dstr> STRING%%configs:       | config configs       ;config:  OBRACE STRINGS EBRACE       | USER    OBRACE STRING EBRACE    { MC_USER_SET(mc_startup->s_configinfo.c_uid, $3) }      | GROUP OBRACE STRING EBRACE      { MC_GROUP_SET(mc_startup->s_configinfo.c_gid, $3) }      | PORT    OBRACE STRING EBRACE    { MC_DIGIT_SET(mc_startup->s_configinfo.c_port, $3) }      | CIPHER  OBRACE STRING EBRACE    { MC_STRING_SET(mc_startup->s_configinfo.c_cipher, $3) }      | BACKLOG OBRACE STRING EBRACE    { MC_DIGIT_SET(mc_startup->s_configinfo.c_backlog, $3) }      | LOGFILE OBRACE STRING EBRACE    { MC_STRING_SET(mc_startup->s_configinfo.c_logfile, $3) }      | PIDFILE OBRACE STRING EBRACE    { MC_STRING_SET(mc_startup->s_configinfo.c_pidfile, $3) }       | KEYFILE OBRACE STRING EBRACE    { MC_STRING_SET(mc_startup->s_configinfo.c_keyfile, $3) }      | CERTFILE  OBRACE STRING EBRACE { MC_STRING_SET(mc_startup->s_configinfo.c_certfile, $3) }      | CYCLETIME OBRACE STRING EBRACE { MC_DIGIT_SET(mc_startup->s_configinfo.c_cycle, $3) }      | IPVERSION OBRACE STRING EBRACE { MC_DOMAIN_SET(mc_startup->s_configinfo.c_domain, $3) }      | MAXREQUESTS OBRACE STRING EBRACE { MC_DIGIT_SET(mc_startup->s_configinfo.c_schd.s_maxfds, $3) }      | MAXPROCESSES OBRACE STRING EBRACE { MC_DIGIT_SET(mc_startup->s_configinfo.c_schd.s_maxprocs, $3) }      | PROCESSCREAT OBRACE STRING EBRACE { MC_DIGIT_SET(mc_startup->s_configinfo.c_schd.s_medianprocs, $3) }      | UPPERTHRESHOLD OBRACE STRING EBRACE { MC_DIGIT_SET(mc_startup->s_configinfo.c_schd.s_thresholdfds_u, $3) }      | LOWERTHRESHOLD OBRACE STRING EBRACE { MC_DIGIT_SET(mc_startup->s_configinfo.c_schd.s_thresholdfds_l, $3) }      ;STRINGS:        | STRING STRINGS       | STRINGS OR STRINGS       ;%%intyyerror(char *s){        fprintf(stderr, "%s\n", s);}
观察yacc的代码,首先第一步是重新定义默认的栈大小,因为默认的栈大小可能比较小会导致溢出,所以此处重新定义一个比较大的值。接下来是一些符号的声明。configs是一个递归的语法,config依次处理各个配置文件的值;OBRACE STRINGS  EBRACE用于过滤不认识的配置,git上的代码并没有这么做,这种做法是mc_httpd上为了过滤其他模块所用的配置参数所用的办法,此处直接给出的yacc和lex代码都是根据mc_httpd的代码改写的。

下一篇博客讨论mc_echo的事件机制的代码以及多进程的模型


 

1 0
原创粉丝点击