├── Makefile ├── README.md ├── fastcgi.c ├── fastcgi.h ├── readme-img ├── 10.png ├── 11.png ├── 13.png ├── 15.png ├── 19.png ├── 20.png ├── 21.png ├── 22.png ├── 23.png ├── 24.png ├── 5.png ├── 6.png ├── 7.png └── 8.png ├── rio.c ├── rio.h ├── schedule ├── lock_test.c ├── mylock.c ├── mylock.h ├── thundering.c └── zhou-m.c ├── server.c ├── server.h ├── test ├── data.txt ├── pic.jpg ├── post.html ├── test-fastcgi.c ├── test.html ├── test.php ├── test_post.php ├── test_upload.php └── upload.html └── zhou.c /Makefile: -------------------------------------------------------------------------------- 1 | zo:rio.c fastcgi.c server.c zhou.c 2 | gcc rio.c fastcgi.c server.c zhou.c -o zo 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ###介绍 3 | C语言实现的一个简单web服务器,只支持GET和POST请求,支持php 4 | 5 | ###运行方法 6 | 在代码目录下执行make,然后执行可执行文件zo,默认端口为8000,同时需要系统已经有php-fpm。 7 | 8 | Ubuntu下可使用下面命令安装: 9 | 10 | `sudo apt-get install php5-fpm` 11 | 12 | 修改php-fpm配置文件设置监听端口: 13 | 14 | `listen =127.0.0.1:9000` 15 | 16 | ###运行效果 17 | 1.运行test目录下的test.html静态页面: 18 | 19 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/5.png) 20 | 21 | 打开浏览器运行结果: 22 | 23 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/6.png) 24 | 25 | 2.运行test目录下的test.php动态页面: 26 | 27 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/21.png) 28 | 29 | 打开浏览器运行结果: 30 | 31 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/7.png) 32 | 33 | 3.运行test目录下的upload.html,该页面包含文件上传,由test_upload.php处理: 34 | 35 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/22.png) 36 | 37 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/23.png) 38 | 39 | 上传的文件data.txt内容为: 40 | 41 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/24.png) 42 | 43 | 打开浏览器运行结果: 44 | 45 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/8.png) 46 | 47 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/10.png) 48 | 49 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/11.png) 50 | 51 | 4.试试运行codeigniter框架。 52 | 53 | 下载ci框架到程序所在目录的ci子目录中。 54 | 55 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/13.png) 56 | 57 | 修改默认Welcome.php控制器文件内容: 58 | 59 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/19.png) 60 | 61 | 打开codeigniter框架默认文件index.php: 62 | 63 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/15.png) 64 | 65 | 66 | 运行welcome控制器的test方法: 67 | 68 | ![](https://github.com/jaykizhou/php-server/blob/master/readme-img/20.png) 69 | 70 | -------------------------------------------------------------------------------- /fastcgi.c: -------------------------------------------------------------------------------- 1 | #include "fastcgi.h" 2 | 3 | /* 4 | * 构造协议记录头部,返回FCGI_Header结构体 5 | */ 6 | FCGI_Header makeHeader( 7 | int type, 8 | int requestId, 9 | int contentLength, 10 | int paddingLength) 11 | { 12 | FCGI_Header header; 13 | header.version = FCGI_VERSION_1; 14 | header.type = (unsigned char) type; 15 | header.requestIdB1 = (unsigned char) ((requestId >> 8) & 0xff); 16 | header.requestIdB0 = (unsigned char) ((requestId ) & 0xff); 17 | header.contentLengthB1 = (unsigned char) ((contentLength >> 8) & 0xff); 18 | header.contentLengthB0 = (unsigned char) ((contentLength ) & 0xff); 19 | header.paddingLength = (unsigned char) paddingLength; 20 | header.reserved = 0; 21 | return header; 22 | } 23 | 24 | /* 25 | * 构造请求开始记录协议体,返回FCGI_BeginRequestBody结构体 26 | */ 27 | FCGI_BeginRequestBody makeBeginRequestBody(int role, int keepConn) 28 | { 29 | FCGI_BeginRequestBody body; 30 | body.roleB1 = (unsigned char) ((role >> 8) & 0xff); 31 | body.roleB0 = (unsigned char) (role & 0xff); 32 | body.flags = (unsigned char) ((keepConn) ? 1 : 0); // 1为长连接,0为短连接 33 | memset(body.reserved, 0, sizeof(body.reserved)); 34 | return body; 35 | } 36 | 37 | /* 38 | * 发送开始请求记录 39 | * 发送成功返回0 40 | * 出错返回-1 41 | */ 42 | int sendBeginRequestRecord(write_record wr, int fd, int requestId) 43 | { 44 | int ret; 45 | // 构造一个FCGI_BeginRequestRecord结构 46 | FCGI_BeginRequestRecord beginRecord; 47 | 48 | beginRecord.header = 49 | makeHeader(FCGI_BEGIN_REQUEST, requestId, sizeof(beginRecord.body), 0); 50 | beginRecord.body = makeBeginRequestBody(FCGI_RESPONDER, 0); 51 | 52 | ret = wr(fd, &beginRecord, sizeof(beginRecord)); 53 | 54 | if (ret == sizeof(beginRecord)) { 55 | return 0; 56 | } else { 57 | return -1; 58 | } 59 | } 60 | 61 | /* 62 | * 发送名值对参数 63 | * 发送成功返回0 64 | * 出错返回-1 65 | */ 66 | int sendParamsRecord( 67 | write_record wr, 68 | int fd, 69 | int requestId, 70 | char *name, 71 | int nlen, 72 | char *value, 73 | int vlen) 74 | { 75 | unsigned char *buf, *old; 76 | int ret, pl, cl = nlen + vlen; 77 | cl = (nlen < 128) ? ++cl : cl + 4; 78 | cl = (vlen < 128) ? ++cl : cl + 4; 79 | 80 | // 计算填充数据长度 81 | pl = (cl % 8) == 0 ? 0 : 8 - (cl % 8); 82 | old = buf = (unsigned char *)malloc(FCGI_HEADER_LEN + cl + pl); 83 | 84 | FCGI_Header nvHeader = makeHeader(FCGI_PARAMS, requestId, cl, pl); 85 | memcpy(buf, (char *)&nvHeader, FCGI_HEADER_LEN); 86 | buf = buf + FCGI_HEADER_LEN; 87 | 88 | if (nlen < 128) { // name长度小于128字节,用一个字节保存长度 89 | *buf++ = (unsigned char)nlen; 90 | } else { // 大于等于128用4个字节保存长度 91 | *buf++ = (unsigned char)((nlen >> 24) | 0x80); 92 | *buf++ = (unsigned char)(nlen >> 16); 93 | *buf++ = (unsigned char)(nlen >> 8); 94 | *buf++ = (unsigned char)nlen; 95 | } 96 | 97 | if (vlen < 128) { // value长度小于128字节,用一个字节保存长度 98 | *buf++ = (unsigned char)vlen; 99 | } else { // 大于等于128用4个字节保存长度 100 | *buf++ = (unsigned char)((vlen >> 24) | 0x80); 101 | *buf++ = (unsigned char)(vlen >> 16); 102 | *buf++ = (unsigned char)(vlen >> 8); 103 | *buf++ = (unsigned char)vlen; 104 | } 105 | 106 | memcpy(buf, name, nlen); 107 | buf = buf + nlen; 108 | memcpy(buf, value, vlen); 109 | 110 | ret = wr(fd, old, FCGI_HEADER_LEN + cl + pl); 111 | 112 | free(old); 113 | 114 | if (ret == (FCGI_HEADER_LEN + cl + pl)) { 115 | return 0; 116 | } else { 117 | return -1; 118 | } 119 | } 120 | 121 | /* 122 | * 发送空的params记录 123 | * 发送成功返回0 124 | * 出错返回-1 125 | */ 126 | int sendEmptyParamsRecord(write_record wr, int fd, int requestId) 127 | { 128 | int ret; 129 | FCGI_Header nvHeader = makeHeader(FCGI_PARAMS, requestId, 0, 0); 130 | ret = wr(fd, (char *)&nvHeader, FCGI_HEADER_LEN); 131 | 132 | if (ret == FCGI_HEADER_LEN) { 133 | return 0; 134 | } else { 135 | return -1; 136 | } 137 | } 138 | 139 | /* 140 | * 发送FCGI_STDIN数据 141 | * 发送成功返回0 142 | * 出错返回-1 143 | */ 144 | int sendStdinRecord( 145 | write_record wr, 146 | int fd, 147 | int requestId, 148 | char *data, 149 | int len) 150 | { 151 | int cl = len, pl, ret; 152 | char buf[8] = {0}; 153 | 154 | while (len > 0) { 155 | // 判断STDIN数据是否大于传输最大值FCGI_MAX_LENGTH 156 | if (len > FCGI_MAX_LENGTH) { 157 | cl = FCGI_MAX_LENGTH; 158 | } 159 | 160 | // 计算填充数据长度 161 | pl = (cl % 8) == 0 ? 0 : 8 - (cl % 8); 162 | 163 | FCGI_Header sinHeader = makeHeader(FCGI_STDIN, requestId, cl, pl); 164 | ret = wr(fd, (char *)&sinHeader, FCGI_HEADER_LEN); // 发送协议头部 165 | if (ret != FCGI_HEADER_LEN) { 166 | return -1; 167 | } 168 | 169 | ret = wr(fd, data, cl); // 发送stdin数据 170 | if (ret != cl) { 171 | return -1; 172 | } 173 | 174 | if (pl > 0) { 175 | ret = wr(fd, buf, pl); // 发送填充数据 176 | if (ret != pl) { 177 | return -1; 178 | } 179 | } 180 | 181 | len -= cl; 182 | data += cl; 183 | } 184 | 185 | return 0; 186 | } 187 | 188 | /* 189 | * 发送空的FCGI_STDIN记录 190 | * 发送成功返回0 191 | * 出错返回-1 192 | */ 193 | int sendEmptyStdinRecord(write_record wr, int fd, int requestId) 194 | { 195 | int ret; 196 | FCGI_Header sinHeader = makeHeader(FCGI_STDIN, requestId, 0, 0); 197 | ret = wr(fd, (char *)&sinHeader, FCGI_HEADER_LEN); 198 | 199 | if (ret == FCGI_HEADER_LEN) { 200 | return 0; 201 | } else { 202 | return -1; 203 | } 204 | } 205 | 206 | /* 207 | * 读取php-fpm处理结果 208 | * 读取成功返回0 209 | * 出错返回-1 210 | */ 211 | int recvRecord( 212 | read_record rr, 213 | send_to_client stc, 214 | int cfd, 215 | int fd, 216 | int requestId/*, 217 | char **sout, 218 | int *outlen, 219 | char **serr, 220 | int *errlen, 221 | FCGI_EndRequestBody *endRequest*/) 222 | { 223 | FCGI_Header responHeader; 224 | FCGI_EndRequestBody endr; 225 | char *conBuf = NULL, *errBuf = NULL; 226 | int buf[8], cl, ret; 227 | int fcgi_rid; // 保存fpm发送过来的request id 228 | 229 | int outlen = 0, errlen = 0; 230 | 231 | //*outlen = 0; 232 | //*errlen = 0; 233 | 234 | // 读取协议记录头部 235 | while (rr(fd, &responHeader, FCGI_HEADER_LEN) > 0) { 236 | fcgi_rid = (int)(responHeader.requestIdB1 << 8) + (int)(responHeader.requestIdB0); 237 | if (responHeader.type == FCGI_STDOUT && fcgi_rid == requestId) { 238 | // 获取内容长度 239 | cl = (int)(responHeader.contentLengthB1 << 8) + (int)(responHeader.contentLengthB0); 240 | //*outlen += cl; 241 | outlen += cl; 242 | 243 | // 如果不是第一次读取FCGI_STDOUT记录 244 | if (conBuf != NULL) { 245 | // 扩展空间 246 | //conBuf = realloc(*sout, *outlen); 247 | conBuf = realloc(conBuf, outlen); 248 | } else { 249 | conBuf = (char *)malloc(cl); 250 | //*sout = conBuf; 251 | } 252 | 253 | ret = rr(fd, conBuf, cl); 254 | if (ret == -1 || ret != cl) { 255 | printf("read fcgi_stdout record error\n"); 256 | return -1; 257 | } 258 | 259 | // 读取填充内容,忽略 260 | if (responHeader.paddingLength > 0) { 261 | ret = rr(fd, buf, responHeader.paddingLength); 262 | if (ret == -1 || ret != responHeader.paddingLength) { 263 | printf("read fcgi_stdout padding error %d\n", responHeader.paddingLength); 264 | return -1; 265 | } 266 | } 267 | } else if (responHeader.type == FCGI_STDERR && fcgi_rid == requestId) { 268 | // 获取内容长度 269 | cl = (int)(responHeader.contentLengthB1 << 8) + (int)(responHeader.contentLengthB0); 270 | //*errlen += cl; 271 | errlen += cl; 272 | 273 | // 如果不是第一次读取FCGI_STDOUT记录 274 | if (errBuf != NULL) { 275 | // 扩展空间 276 | errBuf = realloc(errBuf, errlen); 277 | } else { 278 | errBuf = (char *)malloc(cl); 279 | //*serr = errBuf; 280 | } 281 | 282 | ret = rr(fd, errBuf, cl); 283 | if (ret == -1 || ret != cl) { 284 | return -1; 285 | } 286 | 287 | // 读取填充内容,忽略 288 | if (responHeader.paddingLength > 0) { 289 | ret = rr(fd, buf, responHeader.paddingLength); 290 | if (ret == -1 || ret != responHeader.paddingLength) { 291 | return -1; 292 | } 293 | } 294 | } else if (responHeader.type == FCGI_END_REQUEST && fcgi_rid == requestId) { 295 | // 读取结束请求协议体 296 | ret = rr(fd, &endr, sizeof(FCGI_EndRequestBody)); 297 | 298 | if (ret == -1 || ret != sizeof(FCGI_EndRequestBody)) { 299 | free(conBuf); 300 | free(errBuf); 301 | return -1; 302 | } 303 | 304 | stc(cfd, outlen, conBuf, errlen, errBuf, &endr); 305 | free(conBuf); 306 | free(errBuf); 307 | return 0; 308 | } 309 | } 310 | return 0; 311 | } 312 | 313 | -------------------------------------------------------------------------------- /fastcgi.h: -------------------------------------------------------------------------------- 1 | #ifndef __FASTCGI_H__ 2 | #define __FASTCGI_H__ 3 | #include 4 | #include 5 | #include 6 | 7 | #define FCGI_MAX_LENGTH 0xFFFF // 允许传输的最大数据长度65536 8 | #define FCGI_HOST "127.0.0.1" // php-fpm地址 9 | #define FCGI_PORT 9000 // php-fpm监听的端口地址 10 | 11 | #define FCGI_VERSION_1 1 // fastcgi协议版本 12 | 13 | /* 14 | * fastcgi协议报头 15 | */ 16 | typedef struct { 17 | unsigned char version; // 版本 18 | unsigned char type; // 协议记录类型 19 | unsigned char requestIdB1; // 请求ID 20 | unsigned char requestIdB0; 21 | unsigned char contentLengthB1; // 内容长度 22 | unsigned char contentLengthB0; 23 | unsigned char paddingLength; // 填充字节长度 24 | unsigned char reserved; // 保留字节 25 | } FCGI_Header; 26 | 27 | #define FCGI_HEADER_LEN 8 // 协议包头长度 28 | 29 | /* 30 | * 可用于FCGI_Header的type组件的值 31 | */ 32 | #define FCGI_BEGIN_REQUEST 1 // 请求开始记录类型 33 | #define FCGI_ABORT_REQUEST 2 34 | #define FCGI_END_REQUEST 3 // 响应结束记录类型 35 | #define FCGI_PARAMS 4 // 传输名值对数据 36 | #define FCGI_STDIN 5 // 传输输入数据,例如post数据 37 | #define FCGI_STDOUT 6 // php-fpm响应数据输出 38 | #define FCGI_STDERR 7 // php-fpm错误输出 39 | #define FCGI_DATA 8 40 | 41 | /* 42 | * 请求开始记录的协议体 43 | */ 44 | typedef struct { 45 | unsigned char roleB1; // web服务器期望php-fpm扮演的角色 46 | unsigned char roleB0; 47 | unsigned char flags; // 控制连接响应后是否立即关闭 48 | unsigned char reserved[5]; 49 | } FCGI_BeginRequestBody; 50 | 51 | /* 52 | * 开始请求记录结构,包含开始请求协议头和协议体 53 | */ 54 | typedef struct { 55 | FCGI_Header header; 56 | FCGI_BeginRequestBody body; 57 | } FCGI_BeginRequestRecord; 58 | 59 | /* 60 | * 期望php-fpm扮演的角色值 61 | */ 62 | #define FCGI_RESPONDER 1 63 | #define FCGI_AUTHORIZER 2 64 | #define FCGI_FILTER 3 65 | 66 | // 为1,表示php-fpm响应结束不会关闭该请求连接 67 | #define FCGI_KEEP_CONN 1 68 | 69 | /* 70 | * 结束请求记录的协议体 71 | */ 72 | typedef struct { 73 | unsigned char appStatusB3; 74 | unsigned char appStatusB2; 75 | unsigned char appStatusB1; 76 | unsigned char appStatusB0; 77 | unsigned char protocolStatus; // 协议级别的状态码 78 | unsigned char reserved[3]; 79 | } FCGI_EndRequestBody; 80 | 81 | /* 82 | * 协议级别状态码的值 83 | */ 84 | #define FCGI_REQUEST_COMPLETE 1 // 正常结束 85 | #define FCGI_CANT_MPX_CONN 2 // 拒绝新请求,无法并发处理 86 | #define FCGI_OVERLOADED 3 // 拒绝新请求,资源超负载 87 | #define FCGI_UNKNOWN_ROLE 4 // 不能识别的角色 88 | 89 | /* 90 | * 结束请求记录结构 91 | */ 92 | typedef struct { 93 | FCGI_Header header; 94 | FCGI_EndRequestBody body; 95 | } FCGI_EndRequestRecord; 96 | 97 | typedef struct { 98 | FCGI_Header header; 99 | unsigned char nameLength; 100 | unsigned char valueLength; 101 | unsigned char data[0]; 102 | } FCGI_ParamsRecord; 103 | 104 | /* 105 | * 构造协议记录头部 106 | */ 107 | FCGI_Header makeHeader( 108 | int type, 109 | int requestId, 110 | int contentLength, 111 | int paddingLength); 112 | 113 | /* 114 | * 构造开始请求记录协议体 115 | */ 116 | FCGI_BeginRequestBody makeBeginRequestBody( 117 | int role, 118 | int keepConn); 119 | 120 | // 发送协议记录函数指针 121 | typedef ssize_t (*write_record)(int, void *, size_t); 122 | 123 | /* 124 | * 发送开始请求记录 125 | */ 126 | int sendBeginRequestRecord(write_record wr, int fd, int requestId); 127 | 128 | /* 129 | * 发送名值对参数 130 | */ 131 | int sendParamsRecord( 132 | write_record wr, 133 | int fd, 134 | int requestId, 135 | char *name, 136 | int nlen, 137 | char *value, 138 | int vlen); 139 | 140 | /* 141 | * 发送空的params记录 142 | */ 143 | int sendEmptyParamsRecord(write_record wr, int fd, int requestId); 144 | 145 | /* 146 | * 发送FCGI_STDIN数据 147 | */ 148 | int sendStdinRecord( 149 | write_record wr, 150 | int fd, 151 | int requestId, 152 | char *data, 153 | int len); 154 | 155 | /* 156 | * 发送空的FCGI_STDIN记录 157 | */ 158 | int sendEmptyStdinRecord(write_record wr, int fd, int requestId); 159 | 160 | // 读取协议记录函数指针 161 | typedef ssize_t (*read_record)(int, void *, size_t); 162 | 163 | // 发送php结果给客户端回调函数声明 164 | typedef ssize_t (*send_to_client)(int, int, char *, int, char *, FCGI_EndRequestBody *); 165 | 166 | /* 167 | * 读取php-fpm处理结果 168 | */ 169 | int recvRecord( 170 | read_record rr, 171 | send_to_client stc, 172 | int cfd, 173 | int fd, 174 | int requestId/*, 175 | char **sout, 176 | int *outlen, 177 | char **serr, 178 | int *errlen, 179 | FCGI_EndRequestBody *endRequest*/); 180 | 181 | 182 | #endif 183 | -------------------------------------------------------------------------------- /readme-img/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/10.png -------------------------------------------------------------------------------- /readme-img/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/11.png -------------------------------------------------------------------------------- /readme-img/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/13.png -------------------------------------------------------------------------------- /readme-img/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/15.png -------------------------------------------------------------------------------- /readme-img/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/19.png -------------------------------------------------------------------------------- /readme-img/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/20.png -------------------------------------------------------------------------------- /readme-img/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/21.png -------------------------------------------------------------------------------- /readme-img/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/22.png -------------------------------------------------------------------------------- /readme-img/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/23.png -------------------------------------------------------------------------------- /readme-img/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/24.png -------------------------------------------------------------------------------- /readme-img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/5.png -------------------------------------------------------------------------------- /readme-img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/6.png -------------------------------------------------------------------------------- /readme-img/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/7.png -------------------------------------------------------------------------------- /readme-img/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/readme-img/8.png -------------------------------------------------------------------------------- /rio.c: -------------------------------------------------------------------------------- 1 | #include "rio.h" 2 | 3 | /* 4 | * 从描述符fd中读取n个字节到存储器位置usrbuf 5 | */ 6 | ssize_t rio_readn(int fd, void *usrbuf, size_t n) 7 | { 8 | size_t nleft = n; // 剩下的未读字节数 9 | ssize_t nread; 10 | char *bufp = usrbuf; 11 | 12 | while (nleft > 0) { 13 | if ((nread = read(fd, bufp, nleft)) < 0) { 14 | if (errno == EINTR) { // 被信号处理函数中断返回 15 | nread = 0; 16 | } else { 17 | return -1; // read出错 18 | } 19 | } else if (nread == 0) { // EOF 20 | break; 21 | } 22 | nleft -= nread; 23 | bufp += nread; 24 | } 25 | 26 | return (n - nleft); // 返回已经读取的字节数 27 | } 28 | 29 | /* 30 | * 将usrbuf缓冲区中的前n个字节数据写入fd中 31 | * 该函数会保证n个字节都会写入fd中 32 | */ 33 | ssize_t rio_writen(int fd, void *usrbuf, size_t n) 34 | { 35 | size_t nleft = n; // 剩下的未写入字节数 36 | ssize_t nwritten; 37 | char *bufp = (char *)usrbuf; 38 | 39 | while (nleft > 0) { 40 | if ((nwritten = write(fd, bufp, nleft)) <= 0) { 41 | if (errno == EINTR) { // 被信号处理函数中断返回 42 | nwritten = 0; 43 | } else { // write函数出错 44 | return -1; 45 | } 46 | } 47 | nleft -= nwritten; 48 | bufp += nwritten; 49 | } 50 | 51 | return n; 52 | } 53 | 54 | /* 55 | * 初始化内部缓冲区rio_t结构 56 | */ 57 | void rio_readinitb(rio_t *rp, int fd) 58 | { 59 | rp->rio_fd = fd; 60 | rp->rio_cnt = 0; 61 | rp->rio_bufptr = rp->rio_buf; 62 | } 63 | 64 | /* 65 | * 系统调用read函数的包装函数 66 | * 相对于read,增加了内部缓冲区 67 | */ 68 | static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) 69 | { 70 | int cnt; 71 | 72 | // 内部缓冲区为空,从缓冲区对应的描述符中继续读取字节填满内部缓冲区 73 | while (rp->rio_cnt <= 0) { 74 | rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf)); 75 | 76 | if (rp->rio_cnt < 0) { // 返回-1 77 | if (errno != EINTR) { 78 | return -1; 79 | } 80 | } else if (rp->rio_cnt == 0) { // EOF 81 | return 0; 82 | } else { 83 | rp->rio_bufptr = rp->rio_buf; 84 | } 85 | } 86 | 87 | // 比较调用所需的字节数n与内部缓冲区可读字节数rp->rio_cnt 88 | // 取其中最小值 89 | cnt = n; 90 | if (rp->rio_cnt < n) { 91 | cnt = rp->rio_cnt; 92 | } 93 | memcpy(usrbuf, rp->rio_bufptr, cnt); 94 | rp->rio_bufptr += cnt; 95 | rp->rio_cnt -= cnt; 96 | 97 | return cnt; 98 | } 99 | 100 | /* 101 | * 从文件rp中读取一行数据(包括结尾的换行符),拷贝到usrbuf 102 | * 并用0字符来结束这行数据 103 | */ 104 | ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) 105 | { 106 | int n, rc; 107 | char c, *bufp = usrbuf; 108 | 109 | for (n = 1; n < maxlen; n++) { 110 | if ((rc = rio_read(rp, &c, 1)) == 1) { 111 | *bufp++ = c; 112 | if (c == '\n') { // 读完了一行 113 | break; 114 | } 115 | } else if (rc == 0) { 116 | if (n == 1) { 117 | return 0; // EOF,但没有读取任何数据 118 | } else { 119 | break; //EOF,但已经读取了一些数据 120 | } 121 | } else { // 出错 122 | return -1; 123 | } 124 | } 125 | 126 | *bufp = 0; 127 | return n; 128 | } 129 | 130 | /* 131 | * 从文件rp中读取n字节数据到usrbuf 132 | */ 133 | ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) 134 | { 135 | size_t nleft = n; // 剩下的未读取字节数 136 | ssize_t nread; 137 | char *bufp = usrbuf; 138 | 139 | while (nleft > 0) { 140 | if ((nread = rio_read(rp, bufp, nleft)) < 0) { 141 | if(errno == EINTR) { // 被信号处理程序中断返回 142 | nread = 0; 143 | } else { 144 | return -1; // 读取数据出错 145 | } 146 | } else if(nread == 0) { // EOF 147 | break; 148 | } 149 | nleft -= nread; 150 | bufp += nread; 151 | } 152 | 153 | return (n - nleft); 154 | } 155 | -------------------------------------------------------------------------------- /rio.h: -------------------------------------------------------------------------------- 1 | #ifndef __RIO_H__ 2 | #define __RIO_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define RIO_BUFSIZE 8192 11 | typedef struct { 12 | int rio_fd; // 内部缓冲区对应的描述符 13 | int rio_cnt; // 可以读取的字节数 14 | char *rio_bufptr; // 下一个可以读取的字节地址 15 | char rio_buf[RIO_BUFSIZE]; // 内部缓冲区 16 | } rio_t; 17 | 18 | ssize_t rio_readn(int fd, void *usrbuf, size_t n); 19 | ssize_t rio_writen(int fd, void *usrbuf, size_t n); 20 | void rio_readinitb(rio_t *rp, int fd); 21 | static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n); 22 | ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen); 23 | ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /schedule/lock_test.c: -------------------------------------------------------------------------------- 1 | #include "mylock.h" 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | 8 | int pid; 9 | int i; 10 | 11 | // 初始化一个锁 12 | slock *sl; 13 | sl = lock_init("/test-lock-1"); 14 | if (sl == NULL) { 15 | printf("lock_init error\n"); 16 | return 0; 17 | } 18 | 19 | if ((pid = fork()) == 0) { // 子进程 20 | p(sl, 1); 21 | for (i = 0; i < 10; i++) { 22 | printf("----------------\n"); 23 | fflush(stdout); 24 | sleep(rand() % 3); 25 | printf("----------------\n"); 26 | fflush(stdout); 27 | sleep(rand() % 2); 28 | } 29 | v(sl); 30 | lock_close(sl); 31 | 32 | } else { // 父进程 33 | p(sl, 1); 34 | for (i = 0; i < 10; i++) { 35 | printf("++++++++++++++++\n"); 36 | fflush(stdout); 37 | sleep(rand() % 3); 38 | printf("++++++++++++++++\n"); 39 | fflush(stdout); 40 | sleep(rand() % 2); 41 | } 42 | v(sl); 43 | lock_close(sl); 44 | wait(); 45 | } 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /schedule/mylock.c: -------------------------------------------------------------------------------- 1 | #include "mylock.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct _slock { 12 | sem_t *semp; 13 | char name[LOCK_NAME_LEN]; 14 | }; 15 | 16 | /* 17 | * 初始化并创建一个命名信号量 18 | */ 19 | slock * lock_init(const char *name) { 20 | slock *sl; 21 | 22 | if ((sl = malloc(sizeof(struct _slock))) == NULL) { 23 | return NULL; 24 | } 25 | 26 | memset(sl->name, '\0', sizeof(sl->name)); 27 | strcpy(sl->name, name); 28 | 29 | if ((sl->semp = sem_open(sl->name, O_CREAT, 0644, 1)) == SEM_FAILED) { 30 | free(sl); 31 | return NULL; 32 | } 33 | 34 | return sl; 35 | } 36 | 37 | /* 38 | * 等待信号量 39 | * wait为0,表示非阻塞等待 40 | * wait为1,表示阻塞等待 41 | */ 42 | int p(slock *sl, int wait) { 43 | if (wait) { 44 | return sem_wait(sl->semp); 45 | } else { 46 | return sem_trywait(sl->semp); 47 | } 48 | } 49 | 50 | /* 51 | * 释放占用的信号量 52 | */ 53 | int v(slock *sl) { 54 | return sem_post(sl->semp); 55 | } 56 | 57 | /* 58 | * 关闭命名信号量 59 | * 和释放锁占用的堆空间 60 | */ 61 | void lock_close(slock *sl) { 62 | sem_close(sl->semp); 63 | sem_unlink(sl->name); 64 | free(sl); 65 | } 66 | -------------------------------------------------------------------------------- /schedule/mylock.h: -------------------------------------------------------------------------------- 1 | #ifndef _MYLOCK_H__ 2 | #define _MYLOCK_H__ 3 | 4 | #define LOCK_NAME_LEN 80 5 | 6 | typedef struct _slock slock; 7 | 8 | slock * lock_init(const char *name); 9 | int p(slock *sl, int wait); 10 | int v(slock *sl); 11 | void lock_close(slock *sl); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /schedule/thundering.c: -------------------------------------------------------------------------------- 1 | #include "mylock.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define PROCESS_NUM 4 18 | #define MAX_EVENTS 64 19 | 20 | slock *sl; 21 | 22 | static int create_and_bind(int port) 23 | { 24 | int fd = socket(PF_INET, SOCK_STREAM, 0); 25 | 26 | struct sockaddr_in serveraddr; 27 | serveraddr.sin_family = AF_INET; 28 | serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 29 | serveraddr.sin_port = htons(port); 30 | 31 | bind(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); 32 | 33 | return fd; 34 | } 35 | 36 | static int set_socket_nonblock(int fd) 37 | { 38 | int flags, ret; 39 | if ((flags = fcntl(fd, F_GETFL, 0)) < 0) { 40 | printf("fcntl error\n"); 41 | return -1; 42 | } 43 | flags |= O_NONBLOCK; 44 | if (fcntl(fd, F_SETFL, flags) < 0) { 45 | printf("fcntl error\n"); 46 | return -1; 47 | } 48 | 49 | return 0; 50 | } 51 | 52 | // 信号处理函数 53 | void sig_handler(int sig) 54 | { 55 | lock_close(sl); 56 | } 57 | 58 | // 设置信号的处理函数 59 | void addsig(int sig) 60 | { 61 | struct sigaction sa; 62 | memset(&sa, '\0', sizeof(sa)); 63 | sa.sa_handler = sig_handler; 64 | sigaction(sig, &sa, NULL); 65 | } 66 | 67 | // 子进程执行体 68 | int process_run(int sfd, slock *sl) 69 | { 70 | int efd, ret, i; 71 | int ln = 0; // 连接数 72 | int ls = -1; // 锁函数的返回值 73 | char buf[100]; 74 | 75 | struct epoll_event event; 76 | struct epoll_event events[MAX_EVENTS]; 77 | 78 | // 每个子进程创建自己的epoll fd 79 | efd = epoll_create(MAX_EVENTS); 80 | if (efd < 0) { 81 | printf("epoll_create error\n"); 82 | return -1; 83 | } 84 | 85 | printf("process %d forked\n", getpid()); 86 | 87 | // 事件循环 88 | while (1) { 89 | /* 90 | * 判断连接数是否为0,为0,阻塞等待 91 | * 不为0,非阻塞等待锁 92 | */ 93 | printf("process %d have %d connections\n", getpid(), ln); 94 | 95 | // 判断是否已经获得了锁 96 | if (ls == -1) { // 未获得锁 97 | if (ln > 0) { 98 | ls = p(sl, 0); 99 | printf("process %d get trywait lock\n", getpid()); 100 | } else { 101 | // 阻塞等待锁 102 | ls = p(sl, 1); 103 | if (ls == -1) { 104 | continue; 105 | } 106 | printf("process %d get wait lock\n", getpid()); 107 | } 108 | 109 | if (ls == 0) { 110 | // 获取到锁,将监听套接字sfd加入epoll中 111 | event.data.fd = sfd; 112 | event.events = EPOLLIN; 113 | if (epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event) < 0) { 114 | printf("epoll_ctl error\n"); 115 | printf("\n%s\n", strerror(errno)); 116 | v(sl); 117 | ls = -1; 118 | if (ln == 0) { 119 | continue; 120 | } 121 | } 122 | printf("process %d epoll add sfd : %d\n", getpid(), sfd); 123 | } 124 | } 125 | 126 | int j, n; 127 | n = epoll_wait(efd, events, MAX_EVENTS, -1); 128 | if (n < 1) { 129 | printf("epoll wait error\n"); 130 | return -1; 131 | } 132 | printf("process %d return from epoll_wait %d!\n", getpid(), n); 133 | 134 | //sleep(1); 135 | for (j = 0; j < n; j++) { 136 | 137 | if ((events[j].events & EPOLLERR) || (events[j].events & EPOLLHUP) || 138 | !(events[j].events & EPOLLIN)) { 139 | printf("process %d epoll error\n", getpid()); 140 | printf("\n%d : %s\n", errno, strerror(errno)); 141 | 142 | // 从epoll删除该监听套接字 143 | epoll_ctl(efd, EPOLL_CTL_DEL, events[j].data.fd, NULL); 144 | 145 | // 关闭该套接字 146 | if (events[j].data.fd != sfd) { 147 | close(events[j].data.fd); 148 | } 149 | 150 | // 释放锁 151 | if (ls == 0) { 152 | v(sl); 153 | ls = -1; 154 | } 155 | } 156 | // 有连接请求 157 | else if ((events[j].events & EPOLLIN) && (events[j].data.fd == sfd)) { 158 | struct sockaddr in_addr; 159 | socklen_t in_len; 160 | int infd; 161 | memset(&in_addr, 0, sizeof(struct sockaddr)); 162 | in_len = 1; 163 | 164 | infd = accept(sfd, &in_addr, &in_len); 165 | if (infd == -1) { 166 | printf("process %d accept failed!\n", getpid()); 167 | printf("\n%d : %s\n", errno, strerror(errno)); 168 | v(sl); 169 | continue; 170 | } 171 | ln++; 172 | printf("process %d accept successed, accepted num: %d\n", getpid(), ln); 173 | 174 | // 将接收的套接字加入监听队列 175 | event.data.fd = infd; 176 | event.events = EPOLLIN; 177 | epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event); 178 | 179 | // 删除sfd的监听 180 | epoll_ctl(efd, EPOLL_CTL_DEL, sfd, NULL); 181 | 182 | // 释放锁 183 | v(sl); 184 | ls = -1; 185 | //sleep(1); 186 | } 187 | // 有数据可读 188 | else if (events[j].events & EPOLLIN) { 189 | int n = read(events[j].data.fd, buf, 100); 190 | buf[n] = '\0'; 191 | printf("process %d read : %s\n", getpid(), buf); 192 | 193 | ln--; 194 | epoll_ctl(efd, EPOLL_CTL_DEL, events[j].data.fd, NULL); 195 | close(events[j].data.fd); 196 | 197 | if (!strcasecmp(buf, "bye")) { 198 | lock_close(sl); 199 | exit(0); 200 | } 201 | } 202 | } 203 | } 204 | } 205 | 206 | 207 | int main(int argc, char *argv[]) { 208 | 209 | int sfd, i; 210 | //slock *sl; 211 | 212 | // 创建并绑定套接字 213 | sfd = create_and_bind(5555); 214 | if (sfd == -1) { 215 | printf("create_and_bind error\n"); 216 | return -1; 217 | } 218 | 219 | // 设置套接字为非阻塞模式 220 | if (set_socket_nonblock(sfd) == -1) { 221 | printf("set_socket_nonblock error\n"); 222 | return -1; 223 | } 224 | 225 | if (listen(sfd, SOMAXCONN) < 0 ) { 226 | printf("listen error\n"); 227 | return -1; 228 | } 229 | 230 | // 创建一个锁 231 | sl = lock_init("/thundering-lock-4"); 232 | if (sl == NULL) { 233 | printf("lock_init error\n"); 234 | return -1; 235 | } 236 | 237 | 238 | // 创建多个进程 239 | for (i = 0; i < PROCESS_NUM; i++) { 240 | int pid = fork(); 241 | 242 | // 子进程 243 | if (pid == 0) { 244 | process_run(sfd, sl); 245 | } 246 | } 247 | 248 | // 设置信号处理函数 249 | addsig(SIGINT); 250 | 251 | int status; 252 | wait(&status); 253 | close(sfd); 254 | //lock_close(sl); 255 | 256 | return 0; 257 | } 258 | 259 | -------------------------------------------------------------------------------- /schedule/zhou-m.c: -------------------------------------------------------------------------------- 1 | /* 2 | * zhou.c 一个简单的web服务器(多进程) 3 | * email: yiqianniyiye@126.com 4 | */ 5 | #include "server.h" 6 | #include "mylock.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #define PROCESS_NUM 4 23 | #define MAX_EVENTS 1024 24 | 25 | slock *sl; // 锁结构指针 26 | 27 | // 信号处理函数 28 | void sig_handler(int sig) 29 | { 30 | lock_close(sl); 31 | } 32 | 33 | // 设置信号的处理函数 34 | void addsig(int sig) 35 | { 36 | struct sigaction sa; 37 | memset(&sa, '\0', sizeof(sa)); 38 | sa.sa_handler = sig_handler; 39 | sigaction(sig, &sa, NULL); 40 | } 41 | 42 | // 子进程执行体 43 | int process_run(int sfd, slock *sl) 44 | { 45 | int efd, ret, i; 46 | int ln = 0; // 连接数 47 | int ls = -1; // 锁函数的返回值 48 | char buf[100]; 49 | 50 | struct epoll_event event; 51 | struct epoll_event events[MAX_EVENTS]; 52 | 53 | // 每个子进程创建自己的epoll fd 54 | efd = epoll_create(MAX_EVENTS); 55 | if (efd < 0) { 56 | error_log("epoll_create error", DEBUGARGS); 57 | return -1; 58 | } 59 | 60 | // 事件循环 61 | while (1) { 62 | /* 63 | * 判断连接数是否为0,为0,阻塞等待 64 | * 不为0,非阻塞等待锁 65 | */ 66 | 67 | // 判断是否已经获得了锁 68 | if (ls == -1) { // 未获得锁 69 | if (ln > 0) { 70 | ls = p(sl, 0); 71 | } else { 72 | // 阻塞等待锁 73 | ls = p(sl, 1); 74 | if (ls == -1) { 75 | continue; 76 | } 77 | } 78 | 79 | if (ls == 0) { 80 | // 获取到锁,将监听套接字sfd加入epoll中 81 | event.data.fd = sfd; 82 | event.events = EPOLLIN; 83 | if (epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event) < 0) { 84 | v(sl); 85 | ls = -1; 86 | if (ln == 0) { 87 | continue; 88 | } 89 | } 90 | } 91 | } 92 | 93 | int j, n; 94 | n = epoll_wait(efd, events, MAX_EVENTS, -1); 95 | if (n < 1) { 96 | error_log("epoll_wait error", DEBUGARGS); 97 | return -1; 98 | } 99 | 100 | for (j = 0; j < n; j++) { 101 | 102 | if ((events[j].events & EPOLLERR) || (events[j].events & EPOLLHUP) || 103 | !(events[j].events & EPOLLIN)) { 104 | 105 | // 从epoll删除该监听套接字 106 | epoll_ctl(efd, EPOLL_CTL_DEL, events[j].data.fd, NULL); 107 | 108 | // 关闭该套接字 109 | if (events[j].data.fd != sfd) { 110 | close(events[j].data.fd); 111 | } 112 | 113 | // 释放锁 114 | if (ls == 0) { 115 | v(sl); 116 | ls = -1; 117 | } 118 | } 119 | // 有连接请求 120 | else if ((events[j].events & EPOLLIN) && (events[j].data.fd == sfd)) { 121 | struct sockaddr in_addr; 122 | socklen_t in_len; 123 | int infd; 124 | memset(&in_addr, 0, sizeof(struct sockaddr)); 125 | in_len = 1; 126 | 127 | infd = accept(sfd, &in_addr, &in_len); 128 | if (infd == -1) { 129 | error_log("accept error", DEBUGARGS); 130 | v(sl); 131 | continue; 132 | } 133 | ln++; 134 | 135 | // 将接收的套接字加入监听队列 136 | event.data.fd = infd; 137 | event.events = EPOLLIN; 138 | epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event); 139 | 140 | // 删除sfd的监听 141 | epoll_ctl(efd, EPOLL_CTL_DEL, sfd, NULL); 142 | 143 | // 释放锁 144 | v(sl); 145 | ls = -1; 146 | } 147 | // 有数据可读 148 | else if (events[j].events & EPOLLIN) { 149 | doit(events[j].data.fd); 150 | 151 | ln--; 152 | epoll_ctl(efd, EPOLL_CTL_DEL, events[j].data.fd, NULL); 153 | close(events[j].data.fd); 154 | 155 | } 156 | } 157 | } 158 | } 159 | 160 | 161 | int main(int argc, char *argv[]) 162 | { 163 | int i, listenfd, connfd, port, clientlen; 164 | struct sockaddr_in clientaddr; 165 | 166 | // 检查是否指定了监听端口 167 | if (argc != 2) { 168 | port = PORT; // 使用默认端口 169 | } else { 170 | port = atoi(argv[1]); 171 | } 172 | 173 | listenfd = open_listenfd(port); 174 | if (listenfd < 0) { 175 | error_log("open_listenfd error", DEBUGARGS); 176 | } 177 | 178 | // 创建一个锁 179 | sl = lock_init("/php-server-lock"); 180 | if (sl == NULL) { 181 | error_log("lock_init error", DEBUGARGS); 182 | return -1; 183 | } 184 | 185 | // 创建多个进程 186 | for (i = 0; i < PROCESS_NUM; i++) { 187 | int pid = fork(); 188 | 189 | // 子进程 190 | if (pid == 0) { 191 | process_run(listenfd, sl); 192 | } 193 | } 194 | 195 | // 设置信号处理函数 196 | addsig(SIGINT); 197 | 198 | int status; 199 | wait(&status); 200 | close(listenfd); 201 | 202 | return 0; 203 | } 204 | -------------------------------------------------------------------------------- /server.c: -------------------------------------------------------------------------------- 1 | #include "rio.h" 2 | #include "fastcgi.h" 3 | #include "server.h" 4 | 5 | /* 6 | * 请求头部结构体 7 | * 只存储请求行和类型、长度字段,其他信息忽略 8 | * 也存储了从请求地址中分析出的请求文件名和查询参数 9 | */ 10 | struct http_header { 11 | char uri[256]; // 请求地址 12 | char method[16]; // 请求方法 13 | char version[16]; // 协议版本 14 | char filename[256]; // 请求文件名(包含完整路径) 15 | char name[256]; // 请求文件名(不包含路径,只有文件名) 16 | char cgiargs[256]; // 查询参数 17 | char contype[256]; // 请求体类型 18 | char conlength[16]; // 请求体长度 19 | }; 20 | 21 | /* 22 | * 处理一个HTTP事务 23 | */ 24 | void doit(int fd) 25 | { 26 | int is_static; 27 | struct stat sbuf; 28 | char buf[MAXLINE]; 29 | hhr_t hhr; 30 | rio_t rio; 31 | 32 | memset(&hhr, 0, sizeof(hhr)); 33 | memset(buf, 0, MAXLINE); 34 | 35 | // 读取请求行 36 | rio_readinitb(&rio, fd); 37 | if (rio_readlineb(&rio, buf, MAXLINE) < 0) { 38 | error_log("readlineb error", DEBUGARGS); 39 | } 40 | 41 | // 提取请求方法、请求URI、HTTP版本 42 | sscanf(buf, "%s %s %s", hhr.method, hhr.uri, hhr.version); 43 | 44 | // 只接收GET和POST请求 45 | if (strcasecmp(hhr.method, "GET") && strcasecmp(hhr.method, "POST")) { 46 | clienterror(fd, hhr.method, "501", "Not Implement", 47 | "Zhou does not implement this method"); 48 | return ; 49 | } 50 | 51 | // 读取请求报头 52 | read_requesthdrs(&rio, &hhr); 53 | 54 | // 分析请求uri,获得具体请求文件名和请求参数 55 | is_static = parse_uri(hhr.uri, hhr.filename, hhr.name, hhr.cgiargs); 56 | 57 | // 判断请求文件是否存在 58 | if (stat(hhr.filename, &sbuf) < 0) { 59 | clienterror(fd, hhr.filename, "404", "Not found", 60 | "Zhou couldn't find this file"); 61 | return ; 62 | } 63 | 64 | if (is_static) { // 静态文件 65 | // 判断是否是普通文件及是否有读权限 66 | if (!S_ISREG(sbuf.st_mode) || !(S_IRUSR & sbuf.st_mode)) { 67 | clienterror(fd, hhr.filename, "403", "Forbidden", 68 | "Zhou couldn't read the file"); 69 | return ; 70 | } 71 | serve_static(fd, hhr.filename, sbuf.st_size); 72 | } else { // 动态文件 73 | // 判断是否有执行权限 74 | if (!S_ISREG(sbuf.st_mode) || !(S_IXUSR & sbuf.st_mode)) { 75 | clienterror(fd, hhr.filename, "403", "Forbidden", 76 | "Zhou couldn't run the CGI program"); 77 | } 78 | serve_dynamic(&rio, &hhr); 79 | } 80 | } 81 | 82 | /* 83 | * 读取请求头部信息 84 | * 如果是GET请求,则简单忽略 85 | * 如果是POST请求,则提取请求体类型和长度 86 | */ 87 | void read_requesthdrs(rio_t *rp, hhr_t *hp) 88 | { 89 | char buf[MAXLINE]; 90 | char *start, *end; 91 | 92 | memset(buf, 0, MAXLINE); 93 | if (rio_readlineb(rp, buf, MAXLINE) < 0) { 94 | error_log("rio_readlineb error", DEBUGARGS); 95 | } 96 | 97 | while (strcmp(buf, "\r\n")) { 98 | 99 | start = index(buf, ':'); 100 | // 每行数据包含\r\n字符,需要删除 101 | end = index(buf, '\r'); 102 | 103 | if (start != 0 && end != 0) { 104 | *end = '\0'; 105 | while ((*(start + 1)) == ' ') { 106 | start++; 107 | } 108 | 109 | if (is_contype(buf)) { 110 | strcpy(hp->contype, start + 1); 111 | } else if (is_conlength(buf)) { 112 | strcpy(hp->conlength, start + 1); 113 | } 114 | } 115 | 116 | memset(buf, 0, MAXLINE); 117 | if (rio_readlineb(rp, buf, MAXLINE) < 0) { 118 | error_log("rio_readlineb error", DEBUGARGS); 119 | } 120 | } 121 | 122 | return ; 123 | } 124 | 125 | /* 126 | * 分析请求uri,提取具体文件名和查询参数 127 | * 请求的是静态文件返回1 128 | * 请求的是动态文件返回0 129 | * 默认的服务器根目录就是程序所在目录 130 | * 默认页面是index.html 131 | */ 132 | int parse_uri(char *uri, char *filename, char *name, char *cgiargs) 133 | { 134 | char *ptr, *query;; 135 | char urin[LOCALBUF]; 136 | char *delim = ".php"; // 根据后缀名判断是静态页面还是动态页面 137 | char cwd[LOCALBUF]; 138 | char *dir; 139 | 140 | strcpy(urin, uri); // 不破坏原始字符串 141 | 142 | if (!(query = strstr(urin, delim))) { // 静态页面 143 | strcpy(cgiargs, ""); 144 | 145 | // 删除无用参数 /index.html?123435 146 | ptr = index(urin, '?'); 147 | if (ptr) { 148 | *ptr = '\0'; 149 | } 150 | 151 | strcpy(filename, "."); 152 | strcat(filename, urin); 153 | // 如果以‘/’结尾,自动添加index.html 154 | if (urin[strlen(urin) - 1] == '/') { 155 | strcat(filename, "index.html"); 156 | } 157 | return 1; 158 | } else { // 动态页面 159 | // 提取查询参数 160 | ptr = index(urin, '?'); 161 | if (ptr) { 162 | strcpy(cgiargs, ptr + 1); 163 | *ptr = '\0'; 164 | } else { 165 | // 类似index.php/class/method会提取class/method的参数 166 | if (*(query + sizeof(delim)) == '/') { 167 | strcpy(cgiargs, query + sizeof(delim) + 1); 168 | *(query + sizeof(delim)) = '\0'; 169 | } 170 | } 171 | dir = getcwd(cwd, LOCALBUF); // 获取当前工作目录 172 | strcpy(filename, dir); // 包含完整路径名 173 | strcat(filename, urin); 174 | strcpy(name, urin); // 不包含完整路径名 175 | return 0; 176 | } 177 | } 178 | 179 | /* 180 | * 读取静态文件内容,发送给客户端 181 | */ 182 | void serve_static(int fd, char *filename, int filesize) 183 | { 184 | int srcfd; 185 | char *srcp, filetype[MAXLINE], buf[MAXLINE]; 186 | 187 | // 获取文件MIME类型 188 | get_filetype(filename, filetype); 189 | sprintf(buf, "HTTP/1.1 200 OK\r\n"); 190 | sprintf(buf, "%sServer: Zhou Web Server\r\n", buf); 191 | sprintf(buf, "%sContent-Length: %d\r\n", buf, filesize); 192 | sprintf(buf, "%sContent-Type: %s\r\n\r\n", buf, filetype); 193 | if (rio_writen(fd, buf, strlen(buf)) < 0) { 194 | error_log("write to client error", DEBUGARGS); 195 | } 196 | 197 | if ((srcfd = open(filename, O_RDONLY, 0)) < 0) { 198 | error_log("open file error", DEBUGARGS); 199 | } 200 | 201 | if ((srcp = mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0)) == ((void *) -1)) { 202 | error_log("mmap error", DEBUGARGS); 203 | } 204 | close(srcfd); 205 | if (rio_writen(fd, srcp, filesize) < 0) { 206 | error_log("wirte to client error", DEBUGARGS); 207 | } 208 | 209 | if (munmap(srcp, filesize) < 0) { 210 | error_log("munmap error", DEBUGARGS); 211 | } 212 | } 213 | 214 | /* 215 | * 根据文件后缀名判断文件类型 216 | */ 217 | void get_filetype(char *filename, char *filetype) 218 | { 219 | if (strstr(filename, ".html")) { 220 | strcpy(filetype, "text/html"); 221 | } 222 | else if (strstr(filename, ".gif")) { 223 | strcpy(filetype, "image/gif"); 224 | } 225 | else if (strstr(filename, ".jpg")) { 226 | strcpy(filetype, "image/jpeg"); 227 | } 228 | else if (strstr(filename, ".png")) { 229 | strcpy(filetype, "image/png"); 230 | } 231 | else { 232 | strcpy(filetype, "text/plain"); 233 | } 234 | } 235 | 236 | /* 237 | * 处理动态文件请求 238 | */ 239 | void serve_dynamic(rio_t *rp, hhr_t *hp) { 240 | int sock; 241 | 242 | // 创建一个连接到fastcgi服务器的套接字 243 | sock = open_clientfd(); 244 | 245 | // 发送http请求数据 246 | send_fastcgi(rp, hp, sock); 247 | 248 | // 接收处理结果 249 | recv_fastcgi(rp->rio_fd, sock); 250 | 251 | close(sock); // 关闭与fastcgi服务器连接的套接字 252 | } 253 | 254 | /* 255 | * 接收fastcgi返回的数据 256 | */ 257 | int recv_fastcgi(int fd, int sock) { 258 | int requestId; 259 | char *p; 260 | int n; 261 | 262 | requestId = sock; 263 | 264 | // 读取处理结果 265 | if (recvRecord(rio_readn, send_to_cli, fd, sock, requestId) < 0) { 266 | error_log("recvRecord error", DEBUGARGS); 267 | return -1; 268 | } 269 | 270 | /* 271 | FCGI_EndRequestBody endr; 272 | char *out, *err; 273 | int outlen, errlen; 274 | 275 | requestId = sock; 276 | 277 | // 读取处理结果 278 | if (recvRecord(rio_readn, sock, requestId, &out, &outlen, &err, &errlen, &endr) < 0) { 279 | error_log("recvRecord error", DEBUGARGS); 280 | return -1; 281 | } 282 | 283 | if (outlen > 0) { 284 | p = index(out, '\r'); 285 | n = (int)(p - out); 286 | rio_writen(fd, p + 3, outlen - n - 3); 287 | free(out); 288 | } 289 | 290 | if (errlen > 0) { 291 | rio_writen(fd, err, errlen); 292 | free(err); 293 | } 294 | */ 295 | 296 | return 0; 297 | } 298 | 299 | /* 300 | * php处理结果发送给客户端 301 | */ 302 | int send_to_cli(int fd, int outlen, char *out, 303 | int errlen, char *err, FCGI_EndRequestBody *endr 304 | ) 305 | { 306 | char *p; 307 | int n; 308 | 309 | char buf[MAXLINE]; 310 | sprintf(buf, "HTTP/1.1 200 OK\r\n"); 311 | sprintf(buf, "%sServer: Zhou Web Server\r\n", buf); 312 | sprintf(buf, "%sContent-Length: %d\r\n", buf, outlen + errlen); 313 | sprintf(buf, "%sContent-Type: %s\r\n\r\n", buf, "text/html"); 314 | if (rio_writen(fd, buf, strlen(buf)) < 0) { 315 | error_log("write to client error", DEBUGARGS); 316 | } 317 | 318 | if (outlen > 0) { 319 | p = index(out, '\r'); 320 | n = (int)(p - out); 321 | if (rio_writen(fd, p + 3, outlen - n - 3) < 0) { 322 | error_log("rio_written error", DEBUGARGS); 323 | return -1; 324 | } 325 | } 326 | 327 | if (errlen > 0) { 328 | if (rio_writen(fd, err, errlen) < 0) { 329 | error_log("rio_written error", DEBUGARGS); 330 | return -1; 331 | } 332 | } 333 | } 334 | 335 | /* 336 | * 发送http请求行和请求体数据给fastcgi服务器 337 | */ 338 | int send_fastcgi(rio_t *rp, hhr_t *hp, int sock) 339 | { 340 | int requestId, i, l; 341 | char *buf; 342 | 343 | requestId = sock; 344 | 345 | // params参数名 346 | char *paname[] = { 347 | "SCRIPT_FILENAME", 348 | "SCRIPT_NAME", 349 | "REQUEST_METHOD", 350 | "REQUEST_URI", 351 | "QUERY_STRING", 352 | "CONTENT_TYPE", 353 | "CONTENT_LENGTH" 354 | }; 355 | 356 | // 对应上面params参数名,具体参数值所在hhr_t结构体中的偏移 357 | int paoffset[] = { 358 | (size_t) & (((hhr_t *)0)->filename), 359 | (size_t) & (((hhr_t *)0)->name), 360 | (size_t) & (((hhr_t *)0)->method), 361 | (size_t) & (((hhr_t *)0)->uri), 362 | (size_t) & (((hhr_t *)0)->cgiargs), 363 | (size_t) & (((hhr_t *)0)->contype), 364 | (size_t) & (((hhr_t *)0)->conlength) 365 | }; 366 | 367 | // 发送开始请求记录 368 | if (sendBeginRequestRecord(rio_writen, sock, requestId) < 0) { 369 | error_log("sendBeginRequestRecord error", DEBUGARGS); 370 | return -1; 371 | } 372 | 373 | // 发送params参数 374 | l = sizeof(paoffset) / sizeof(paoffset[0]); 375 | for (i = 0; i < l; i++) { 376 | // params参数的值不为空才发送 377 | if (strlen((char *)(((int)hp) + paoffset[i])) > 0) { 378 | if (sendParamsRecord(rio_writen, sock, requestId, paname[i], strlen(paname[i]), 379 | (char *)(((int)hp) + paoffset[i]), 380 | strlen((char *)(((int)hp) + paoffset[i]))) < 0) { 381 | error_log("sendParamsRecord error", DEBUGARGS); 382 | return -1; 383 | } 384 | } 385 | } 386 | 387 | // 发送空的params参数 388 | if (sendEmptyParamsRecord(rio_writen, sock, requestId) < 0) { 389 | error_log("sendEmptyParamsRecord error", DEBUGARGS); 390 | return -1; 391 | } 392 | 393 | // 继续读取请求体数据 394 | l = atoi(hp->conlength); 395 | if (l > 0) { // 请求体大小大于0 396 | buf = (char *)malloc(l + 1); 397 | memset(buf, '\0', l); 398 | if (rio_readnb(rp, buf, l) < 0) { 399 | error_log("rio_readn error", DEBUGARGS); 400 | free(buf); 401 | return -1; 402 | } 403 | 404 | // 发送stdin数据 405 | if (sendStdinRecord(rio_writen, sock, requestId, buf, l) < 0) { 406 | error_log("sendStdinRecord error", DEBUGARGS); 407 | free(buf); 408 | return -1; 409 | } 410 | 411 | free(buf); 412 | } 413 | 414 | // 发送空的stdin数据 415 | if (sendEmptyStdinRecord(rio_writen, sock, requestId) < 0) { 416 | error_log("sendEmptyStdinRecord error", DEBUGARGS); 417 | return -1; 418 | } 419 | 420 | return 0; 421 | } 422 | 423 | /* 424 | * 创建一个套接字,然后在指定端口监听 425 | * 返回监听套接字,出错返回-1 426 | */ 427 | int open_listenfd(int port) 428 | { 429 | int listenfd, optval = 1; 430 | struct sockaddr_in serveraddr; 431 | 432 | // 创建一个套接字 433 | if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 434 | return -1; 435 | } 436 | 437 | // 设置重启后可以重新使用监听端口 438 | if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, 439 | (const void *)&optval, sizeof(int)) < 0) { 440 | return -1; 441 | } 442 | 443 | // listenfd可以接受port端口上来自任何地址的请求 444 | bzero((char *)&serveraddr, sizeof(serveraddr)); 445 | serveraddr.sin_family = AF_INET; 446 | serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 447 | serveraddr.sin_port = htons((unsigned short)port); 448 | 449 | if (bind(listenfd, (SA *)&serveraddr, sizeof(serveraddr)) < 0) { 450 | return -1; 451 | } 452 | 453 | // 等待请求连接 454 | if (listen(listenfd, LISTENQ) < 0) { 455 | return -1; 456 | } 457 | 458 | return listenfd; 459 | } 460 | 461 | /* 462 | * 创建连接fastcgi服务器的客户端套接字 463 | * 出错返回-1 464 | */ 465 | int open_clientfd() { 466 | int sock; 467 | struct sockaddr_in serv_addr; 468 | 469 | // 创建套接字 470 | sock = socket(PF_INET, SOCK_STREAM, 0); 471 | if (-1 == sock) { 472 | error_log("socket error", DEBUGARGS); 473 | return -1; 474 | } 475 | 476 | memset(&serv_addr, 0, sizeof(serv_addr)); 477 | serv_addr.sin_family = AF_INET; 478 | serv_addr.sin_addr.s_addr = inet_addr(FCGI_HOST); 479 | serv_addr.sin_port = htons(FCGI_PORT); 480 | 481 | // 连接服务器 482 | if(-1 == connect(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr))){ 483 | error_log("connect error", DEBUGARGS); 484 | return -1; 485 | } 486 | 487 | return sock; 488 | } 489 | 490 | void clienterror(int fd, char *cause, char *errnum, 491 | char *shortmsg, char *longmsg) 492 | { 493 | char buf[MAXLINE], body[MAXBUF]; 494 | 495 | // 构建http响应体 496 | sprintf(body, "Server Error"); 497 | sprintf(body, "%s\r\n", body); 498 | sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg); 499 | sprintf(body, "%s

%s: %s

\r\n", body, longmsg, cause); 500 | sprintf(body, "%s
The Zhou Web Server\r\n", body); 501 | 502 | // 发送http响应 503 | sprintf(buf, "HTTP/1.1 %s %s\r\n", errnum, shortmsg); 504 | rio_writen(fd, buf, strlen(buf)); 505 | sprintf(buf, "Content-Type: text/html\r\n"); 506 | rio_writen(fd, buf, strlen(buf)); 507 | sprintf(buf, "Content-Length: %d\r\n\r\n", strlen(body)); 508 | rio_writen(fd, buf, strlen(buf)); 509 | rio_writen(fd, body, strlen(body)); 510 | } 511 | 512 | /* 513 | * 打印错误信息 514 | */ 515 | void error_log(const char *msg, const char *filename, 516 | int line, const char *func) 517 | { 518 | fprintf(stderr, "%s(%d)-%s a error happen -> %s:%s\n", filename, line, 519 | func, msg, strerror(errno)); 520 | exit(0); 521 | } 522 | 523 | /* 524 | * 将str前n个字符转换为小写 525 | */ 526 | static void strtolow(char *str, int n) 527 | { 528 | char *cur = str; 529 | while (n > 0) { 530 | *cur = tolower(*cur); 531 | cur++; 532 | n--; 533 | } 534 | } 535 | 536 | /* 537 | * 判断str起始位置开始是否包含"content-type" 538 | * 包含返回1 539 | * 不包含返回0 540 | */ 541 | static int is_contype(char *str) 542 | { 543 | char *cur = str; 544 | char *cmp = "content-type"; 545 | 546 | // 删除开始的空格 547 | while (*cur == ' ') { 548 | cur++; 549 | } 550 | 551 | for (; *cmp != '\0' && tolower(*cur) == *cmp; cur++,cmp++) 552 | ; 553 | 554 | if (*cmp == '\0') { // cmp字符串以0结束 555 | return 1; 556 | } 557 | 558 | return 0; 559 | 560 | } 561 | 562 | /* 563 | * 判断str起始位置开始是否包含"content-length" 564 | * 包含返回1 565 | * 不包含返回0 566 | */ 567 | static int is_conlength(char *str) 568 | { 569 | char *cur = str; 570 | char *cmp = "content-length"; 571 | 572 | // 删除开始的空格 573 | while (*cur == ' ') { 574 | cur++; 575 | } 576 | 577 | for (; *cmp != '\0' && tolower(*cur) == *cmp; cur++,cmp++) 578 | ; 579 | 580 | if (*cmp == '\0') { // cmp字符串以0结束 581 | return 1; 582 | } 583 | 584 | return 0; 585 | 586 | } 587 | -------------------------------------------------------------------------------- /server.h: -------------------------------------------------------------------------------- 1 | #ifndef __SERVER_H__ 2 | #define __SERVER_H__ 3 | #include "rio.h" 4 | #include "fastcgi.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define MAXLINE 8192 // 最大行长度 18 | #define MAXBUF 8192 // io缓冲区最大值 19 | #define LOCALBUF 1024 // 局部缓冲区大小 20 | 21 | typedef struct sockaddr SA; 22 | typedef struct http_header hhr_t; 23 | 24 | #define LISTENQ 1024 // listen函数的第二个参数 25 | int open_listenfd(int port); 26 | int open_clientfd(); 27 | 28 | #define PORT 8000 // 默认端口号 29 | 30 | // error_log函数的最后三个参数 31 | #define DEBUGARGS __FILE__,__LINE__,__FUNCTION__ 32 | // 错误信息处理 33 | void error_log(const char *msg, const char *filename, 34 | int line, const char *func); 35 | 36 | void doit(int fd); // 处理一个http事务 37 | void read_requesthdrs(rio_t *rp, hhr_t *hp); // 读取请求行 38 | 39 | // 分析uri 40 | int parse_uri(char *uri, char *filename, char *name, char *cgiargs); 41 | 42 | // 静态文件请求处理 43 | void serve_static(int fd, char *filename, int filesize); 44 | 45 | // 获取请求文件MIME类型 46 | void get_filetype(char *filename, char *filetype); 47 | 48 | // 动态文件请求处理 49 | void serve_dynamic(rio_t *rp, hhr_t *hp); 50 | 51 | /* 52 | * 向客户端发送一个HTTP响应,其中包含相应的状态码和状态信息 53 | * 响应主体html文件包含具体错误信息 54 | */ 55 | void clienterror(int fd, char *cause, char *errnum, 56 | char *shortmsg, char *longmsg); 57 | 58 | // 将php结果发送给客户端的回调函数 59 | int send_to_cli(int fd, int outlen, char *out, 60 | int errlen, char *err, FCGI_EndRequestBody *endr 61 | ); 62 | 63 | // 将str前n个字符转换为小写 64 | static void strtolow(char *str, int n); 65 | 66 | // 判断str起始位置开始是否包含"content-type" 67 | static int is_contype(char *str); 68 | 69 | // 判断str起始位置开始是否包含"content-length" 70 | static int is_conlength(char *str); 71 | 72 | // 发送http请求行和请求体数据给fastcgi服务器 73 | int send_fastcgi(rio_t *rp, hhr_t *hp, int sock); 74 | 75 | // 接收fastcgi返回的数据 76 | int recv_fastcgi(int fd, int sock); 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /test/data.txt: -------------------------------------------------------------------------------- 1 | 123456789 2 | abcdefghi 3 | 4 | hello world 5 | -------------------------------------------------------------------------------- /test/pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaykizhou/php-server/191a6a30e3786a610c5a94706dd0558762cb0667/test/pic.jpg -------------------------------------------------------------------------------- /test/post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | post 4 | 5 | 6 |
7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /test/test-fastcgi.c: -------------------------------------------------------------------------------- 1 | #include "../rio.h" 2 | #include "../fastcgi.h" 3 | #include 4 | #include 5 | 6 | char *post_data = 7 | "------WebKitFormBoundaryabcdefgh\r\n" 8 | "Content-Disposition: form-data; name=\"name\"\r\n" 9 | "\r\n" 10 | "a\r\n" 11 | "------WebKitFormBoundaryabcdefgh\r\n" 12 | "Content-Disposition: form-data; name=\"age\"\r\n" 13 | "\r\n" 14 | "12\r\n" 15 | "------WebKitFormBoundaryabcdefgh\r\n" 16 | "Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n" 17 | "Content-Type: text/plain\r\n" 18 | "\r\n" 19 | "123456789\r\n" 20 | "abcdef\r\n" 21 | "123456\r\n" 22 | "------WebKitFormBoundaryabcdefgh--\r\n" 23 | ; 24 | 25 | /* 26 | * php处理结果发送给客户端 27 | */ 28 | int send_to_cli(int fd, int outlen, char *out, 29 | int errlen, char *err, FCGI_EndRequestBody *endr 30 | ) 31 | { 32 | char *p; 33 | int n; 34 | 35 | if (outlen > 0) { 36 | p = index(out, '\r'); 37 | n = (int)(p - out); 38 | if (rio_writen(fd, p + 3, outlen - n - 3) < 0) { 39 | return -1; 40 | } 41 | } 42 | 43 | if (errlen > 0) { 44 | if (rio_writen(fd, err, errlen) < 0) { 45 | return -1; 46 | } 47 | } 48 | } 49 | 50 | int main() 51 | { 52 | int sock, i, requestId = 1; 53 | struct sockaddr_in serv_addr; 54 | char msg[20]; 55 | 56 | printf("post data lenght is 357: %d\n", strlen(post_data)); 57 | 58 | char *params[][2] = { 59 | {"SCRIPT_FILENAME", "/home/zhou/php-server/test/test.php"}, 60 | {"REQUEST_METHOD", "POST"}, 61 | //{"QUERY_STRING", "name=test"}, 62 | {"CONTENT_TYPE", "multipart/form-data;boundary=----WebKitFormBoundaryabcdefgh"}, 63 | {"CONTENT_LENGTH", "357"}, 64 | {"",""} 65 | }; 66 | 67 | // 创建套接字 68 | sock = socket(PF_INET, SOCK_STREAM, 0); 69 | if (-1 == sock) { 70 | printf("error \n"); 71 | exit(-1); 72 | } 73 | 74 | memset(&serv_addr, 0, sizeof(serv_addr)); 75 | serv_addr.sin_family = AF_INET; 76 | serv_addr.sin_addr.s_addr = inet_addr(FCGI_HOST); 77 | serv_addr.sin_port = htons(FCGI_PORT); 78 | 79 | // 连接服务器 80 | if(-1 == connect(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr))){ 81 | printf("sock error \n"); 82 | exit(-1); 83 | } 84 | 85 | // 发送开始请求记录 86 | if (sendBeginRequestRecord(rio_writen, sock, requestId) < 0) { 87 | printf("sendBeginRequestRecord error\n"); 88 | return -1; 89 | } 90 | 91 | // 发送params参数 92 | for (i = 0; params[i][0] != ""; i++) { 93 | if (sendParamsRecord(rio_writen, sock, requestId, params[i][0], strlen(params[i][0]), 94 | params[i][1], strlen(params[i][1])) < 0) { 95 | printf("sendParamsRecord error\n"); 96 | return -1; 97 | } 98 | } 99 | printf("sendParamsRecord complete!\n"); 100 | 101 | // 发送空的params参数 102 | if (sendEmptyParamsRecord(rio_writen, sock, requestId) < 0) { 103 | printf("sendEmptyParamsRecord error\n"); 104 | return -1; 105 | } 106 | 107 | // 发送stdin数据 108 | if (sendStdinRecord(rio_writen, sock, requestId, post_data, strlen(post_data)) < 0) { 109 | printf("sendStdinRecord error\n"); 110 | return -1; 111 | } 112 | 113 | // 发送空的stdin数据 114 | if (sendEmptyStdinRecord(rio_writen, sock, requestId) < 0) { 115 | printf("sendEmptyStdinRecord error\n"); 116 | return -1; 117 | } 118 | 119 | printf("sendEmptyRecord and Stdin complete!\n"); 120 | 121 | // 读取处理结果 122 | if (recvRecord(rio_readn, send_to_cli, 1, sock, requestId) < 0) { 123 | return -1; 124 | } 125 | 126 | return 0; 127 | } 128 | -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | test 4 | 5 | 6 |

test is test

7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/test.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | post 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /zhou.c: -------------------------------------------------------------------------------- 1 | /* 2 | * zhou.c 一个简单的web服务器 3 | * email: yiqianniyiye@126.com 4 | */ 5 | #include "server.h" 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | int listenfd, connfd, port, clientlen; 10 | struct sockaddr_in clientaddr; 11 | 12 | // 检查是否指定了监听端口 13 | if (argc != 2) { 14 | port = PORT; // 使用默认端口 15 | } else { 16 | port = atoi(argv[1]); 17 | } 18 | 19 | listenfd = open_listenfd(port); 20 | if (listenfd < 0) { 21 | error_log("open_listenfd error", DEBUGARGS); 22 | } 23 | 24 | while (1) { 25 | clientlen = sizeof(clientaddr); 26 | connfd = accept(listenfd, (SA *)&clientaddr, &clientlen); 27 | if (connfd < 0) { 28 | error_log("accept error", DEBUGARGS); 29 | } 30 | doit(connfd); 31 | close(connfd); 32 | } 33 | 34 | return 0; 35 | } 36 | --------------------------------------------------------------------------------