最近看了《HTTP权威指南》和《UNP》有了写一个简单的web服务器的想法,正好这个学期没有什么课,所以就花了一个星期这样写了一个出来,鉴于本人水平有限,如果有什么设计或代码错误的,希望各位指出哈。
tinyhttp web服务器的架构为epoll + 多线程 + sendfile, 本来想用线程池代替的因为每来一个连接就new一个线程这样对于OS来说负担太大,并且线程一旦过多线程切换就会花费很大代价造成性能瓶颈,但是我打算之后单独写一个线程池代码示例的说,所以这个版本就使用多线程来代替线程池了。
tinyhttp暂时只支持GET和HEAD方法,支持的首部不多大概七八个吧,支持伪长连接(我觉得是伪的哈哈)。
话不多说,先上几张效果图吧:
这张是测试http请求和响应包的
这张是自己构造了一个包来收发的
这张是我把google首页的源代码拿来测试的
这张是我自己写了一个html代码使用火狐浏览器来与的我tinyhttp web服务其通信的测试
我的tinyhttp是可配置的,现阶段只支持domain和docroot配置项,domain就是你部署的网站域名,docroot想必学过网页的都知道是什么意思吧~
好了,现在就把完整的源代码给出,总共有1500+行:
1 /* 2 *Author Zou Xiao hang 3 *Email [email protected] 4 *File Name tinyhttp.h 5 *Date 2013/10/05 6 */ 7 #ifndef _TINY_HTTP_H_ 8 #define _TINY_HTTP_H_ 9 10 #include <sys/socket.h> 11 #include <sys/types.h> 12 #include <stdlib.h> 13 #include <stdio.h> 14 #include <netdb.h> 15 #include <sys/epoll.h> 16 #include <strings.h> 17 #include <string> 18 #include <errno.h> 19 #include <unistd.h> 20 #include <fcntl.h> 21 #include <utility> 22 #include <fstream> 23 #include <sstream> 24 #include <map> 25 #include <iostream> 26 #include <string.h> 27 #include <pthread.h> 28 #include <netinet/tcp.h> 29 #include <time.h> 30 #include <sys/stat.h> 31 #include <sys/sendfile.h> 32 33 #include "utility.h" 34 #include "parse.h" 35 36 using namespace std; 37 38 typedef struct _epollfd_connfd 39 { 40 int epollfd; 41 int connfd; 42 }_epollfd_connfd; 43 44 /*********************************** 常数定义 45 *******************************************/ 46 #define MAX_EVENTS 1024 //epoll最大监听事件数 47 #define MAX_BACKLOG 100 //监听队列最大数 48 /****************************************************************************** 49 ***********/ 50 51 /*********************************** 保存配置文件相关值 52 *********************************/ 53 string tyhp_domain(""); 54 string tyhp_docroot(""); 55 /****************************************************************************** 56 ***********/ 57 58 /*********************************** MIME定义 59 *******************************************/ 60 typedef struct mime_node 61 { 62 const char *type; 63 const char *value; 64 }mime_node; 65 66 mime_node tyhp_mime[] = 67 { 68 {".html", "text/html"}, 69 {".xml", "text/xml"}, 70 {".xhtml", "application/xhtml+xml"}, 71 {".txt", "text/plain"}, 72 {".rtf", "application/rtf"}, 73 {".pdf", "application/pdf"}, 74 {".word", "application/msword"}, 75 {".png", "image/png"}, 76 {".gif", "image/gif"}, 77 {".jpg", "image/jpeg"}, 78 {".jpeg", "image/jpeg"}, 79 {".au", "audio/basic"}, 80 {".mpeg", "video/mpeg"}, 81 {".mpg", "video/mpeg"}, 82 {".avi", "video/x-msvideo"}, 83 {".gz", "application/x-gzip"}, 84 {".tar", "application/x-tar"}, 85 {NULL ,NULL} 86 }; 87 /* 88 *函数作用:将MIME的type转换为相应的value 89 *函数参数:type 90 *函数返回值: NULL表示type在MIME中不存在,否则返回相应value的指针 91 */ 92 inline const char* tyhp_mime_type2value(const char *type) 93 { 94 for(int i = 0; tyhp_mime[i].type != NULL; ++i) 95 { 96 if(strcmp(type, tyhp_mime[i].type) == 0) 97 return tyhp_mime[i].value; 98 } 99 return NULL; 100 } 101 /****************************************************************************** 102 ***********/ 103 104 /*********************************** HTTP状态码 105 *******************************************/ 106 #define TYHP_CONTINUE 100 //收到了请求的起始部分,客户端应该继续请求 107 108 #define TYHP_OK 200 //服务器已经成功处理请求 109 #define TYHP_ACCEPTED 202 //请求已接受,服务器尚未处理 110 111 #define TYHP_MOVED 301 //请求的URL已移走,响应应该包含Location URL 112 #define TYHP_FOUND 302 //请求的URL临时移走,响应应该包含Location URL 113 #define TYHP_SEEOTHER 303 //告诉客户端应该用另一个URL获取资源,响应应该包含 114 Location URL 115 #define TYHP_NOTMODIFIED 304 //资源未发生变化 116 117 #define TYHP_BADREQUEST 400 //客户端发送了一条异常请求 118 #define TYHP_FORBIDDEN 403 //服务器拒绝请求 119 #define TYHP_NOTFOUND 404 //URL未找到 120 121 122 #define TYHP_ERROR 500 //服务器出错 123 #define TYHP_NOIMPLEMENTED 501 //服务器不支持当前请求所需要的某个功能 124 #define TYHP_BADGATEWAY 502 125 //作为代理或网关使用的服务器遇到了来自响应链中上游的无效响应 126 #define TYHP_SRVUNAVAILABLE 503 127 //服务器目前无法提供请求服务,过一段时间后可以恢复 128 129 char tyhp_ok[] = "OK"; 130 char tyhp_badrequest[] = "Bad Request"; 131 char tyhp_forbidden[] = "Forbidden"; 132 char tyhp_notfound[] = "Not Found"; 133 char tyhp_noimplemented[] = "No Implemented"; 134 135 /* 136 *函数作用:通过HTTP状态码返回友好语句 137 *函数参数:HTTP状态码 138 *函数返回值: 相应的语句 139 */ 140 char *tyhp_get_state_by_codes(int http_codes); 141 /****************************************************************************** 142 ************/ 143 144 /*********************************** HTTP响应首部 145 *******************************************/ 146 #define TYHP_ACCEPTRANGE_HEAD "Accpet-Range" 147 #define TYHP_AGE_HEAD "Age" 148 #define TYHP_ALLOW_HEAD "Allow" 149 #define TYHP_CONTENTBASE_HEAD "Content-Base" 150 #define TYHP_CONTENTLENGTH_HEAD "Content-Length" 151 #define TYHP_CONTENTLOCATION_HEAD "Content-Location" 152 #define TYHP_CONTENTRANGE_HEAD "Content-Range" 153 #define TYHP_CONTENTTYPE_HEAD "Content-Type" 154 #define TYHP_DATE_HEAD "Date" 155 #define TYHP_EXPIRES_HEAD "Expires" 156 #define TYHP_LAST_MODIFIED_HEAD "Last-Modified" 157 #define TYHP_LOCATION_HEAD "Location" 158 #define TYHP_PUBLIC_HEAD "Public" 159 #define TYHP_RANGE_HEAD "Range" 160 #define TYHP_SERVER_HEAD "Server" 161 /****************************************************************************** 162 ************/ 163 /*********************************** HTTP请求首部 164 *******************************************/ 165 //#define TYHP 166 /****************************************************************************** 167 ************/ 168 169 #endif
1 /* 2 *Author Zou Xiao hang 3 *Email [email protected] 4 *File Name tinyhttp.cpp 5 *Date 2013/10/05 6 */ 7 #include "tinyhttp.h" 8 9 #define ONEKILO 1024 10 #define ONEMEGA 1024*ONEKILO 11 #define ONEGIGA 1024*ONEMEGA 12 13 /*********************************** 线程相关 *******************************************/ 14 /* 15 *函数作用:处理客户端链接的线程例程 16 *函数参数:param为客户conn_sock 17 *函数返回值: 无 18 */ 19 void* tyhp_thread_func(void *param); 20 21 22 //记录当前处理线程的数量 23 int32_t tyhp_thread_num = 0; 24 pthread_mutex_t tyhp_thread_num_mutex = PTHREAD_MUTEX_INITIALIZER; 25 /* 26 *函数作用:tyhp_thread_num原子加1 27 *函数参数:无 28 *函数返回值: 无 29 */ 30 void tyhp_thread_num_add1(); 31 /* 32 *函数作用:tyhp_thread_num原子减1 33 *函数参数:无 34 *函数返回值: 无 35 */ 36 void tyhp_thread_num_minus1(); 37 /* 38 *函数作用:tyhp_thread_num原子读 39 *函数参数:无 40 *函数返回值: tyhp_thread_num当前值 41 */ 42 int32_t tyhp_thread_num_read(); 43 /*****************************************************************************************/ 44 45 /******************************** tyhp_http_header_t处理函数 *********************************/ 46 /* 47 *函数作用:根据解析下来的tyhp_http_header_t来处理客户的请求 48 *函数参数: phttphdr指向要处理的tyhp_http_header_t 49 out保存了处理的结果,即http响应包 50 *函数返回值: HTTP状态码 51 */ 52 int tyhp_do_http_header(tyhp_http_header_t *phttphdr, string& out); 53 /* 54 *函数作用:通过HTTP状态码返回友好语句 55 *函数参数:HTTP状态码 56 *函数返回值: 相应的语句 57 */ 58 char *tyhp_get_state_by_codes(int http_codes); 59 /*****************************************************************************************/ 60 61 /*********************************** web服务器程序入口函数 ***************************************/ 62 int main(int argc, char const *argv[]) 63 { 64 int listen_fd; 65 int conn_sock; 66 int nfds; 67 int epollfd; 68 uint16_t listen_port; 69 struct servent *pservent; 70 struct epoll_event ev; 71 struct epoll_event events[MAX_EVENTS]; 72 struct sockaddr_in server_addr; 73 struct sockaddr_in client_addr; 74 socklen_t addrlen; 75 pthread_attr_t pthread_attr_detach; 76 _epollfd_connfd epollfd_connfd; 77 pthread_t tid; 78 79 if(argc != 2) 80 { 81 printf("Usage: %s <config_path>\n", argv[0]); 82 exit(-1); 83 } 84 //判断配置文件是否存在 85 if(-1 == tyhp_is_file_existed(argv[1])) 86 { 87 perror("tyhp_is_file_existed"); 88 exit(-1); 89 } 90 //调用tyhp_parse_config解析配置文件 91 if(-1 == tyhp_parse_config(argv[1])) 92 { 93 printf("tyhp_parse_config error\n"); 94 exit(-1); 95 } 96 97 //创建监听套接字 98 listen_fd = tyhp_socket(AF_INET, SOCK_STREAM, 0); 99 //设置监听套接字为非阻塞模式 100 tyhp_set_nonblocking(listen_fd); 101 //对监听套接字设置SO_REUSEADDR选项 102 tyhp_set_reuse_addr(listen_fd); 103 //通过服务名和协议名获得相应的知名端口,其实可以直接设置端口为80,我们这样做是为了可扩展性 104 pservent = tyhp_getservbyname("http", "tcp"); 105 //pservent->s_port已经是网络字节序了 106 listen_port = pservent->s_port; 107 108 bzero(&server_addr, sizeof(server_addr)); 109 server_addr.sin_family = AF_INET; 110 server_addr.sin_port = (listen_port); 111 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 112 113 //将服务器sockaddr_in与监听套接字绑定 114 tyhp_bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); 115 //开始监听 116 tyhp_listen(listen_fd, MAX_BACKLOG); 117 118 //创建epoll文件描述符 119 epollfd = tyhp_epoll_create(MAX_EVENTS); 120 121 ev.events = EPOLLIN;//可读事件 122 ev.data.fd = listen_fd; 123 //将监听事件加入epoll中 124 tyhp_epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_fd, &ev); 125 126 //设置线程属性为detach 127 pthread_attr_init(&pthread_attr_detach); 128 pthread_attr_setdetachstate(&pthread_attr_detach, PTHREAD_CREATE_DETACHED); 129 130 for(;;) 131 { 132 //无限等待直到有描述符就绪 133 nfds = tyhp_epoll_wait(epollfd, events, MAX_EVENTS, -1); 134 //若tyhp_epoll_wait被中断则重新调用该函数 135 if(nfds == -1 && errno == EINTR) 136 continue; 137 138 for(int n = 0; n != nfds; ++n) 139 { 140 //处理监听套接字触发的事件 141 if(events[n].data.fd == listen_fd) 142 { 143 conn_sock = tyhp_accept(listen_fd, (struct sockaddr*)&client_addr, &addrlen); 144 //设置新链接上的套接字为非阻塞模式 145 tyhp_set_nonblocking(conn_sock); 146 //设置读事件和ET模式 147 ev.events = EPOLLIN | EPOLLET; 148 ev.data.fd = conn_sock; 149 //将监听事件加入epoll中 150 tyhp_epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev); 151 } 152 else 153 { 154 epollfd_connfd.epollfd = epollfd; 155 epollfd_connfd.connfd = events[n].data.fd; 156 ev.data.fd = conn_sock; 157 //epoll不再监听这个客户端套接字 158 tyhp_epoll_ctl(epollfd, EPOLL_CTL_DEL, conn_sock, &ev); 159 //处理链接 160 pthread_create(&tid, &pthread_attr_detach, &tyhp_thread_func, (void*)&epollfd_connfd); 161 //tyhp_thread_func((void*)&epollfd_connfd); 162 //close(conn_sock); 163 } 164 } 165 } 166 //清理工作 167 pthread_attr_destroy(&pthread_attr_detach); 168 169 //关闭监听套接字 170 close(listen_fd); 171 return 0; 172 } 173 /*****************************************************************************************/ 174 175 /*********************************** 线程相关 *******************************************/ 176 /* 177 *函数作用:处理客户端链接的线程例程 178 *函数参数: 179 *函数返回值: NULL 180 */ 181 #define TIMEOUT 1000*60*4 //设置超时 milliseconds 182 void* tyhp_thread_func(void *param) 183 { 184 tyhp_thread_num_add1(); 185 186 tyhp_http_header_t *phttphdr = tyhp_alloc_http_header(); 187 188 _epollfd_connfd *ptr_epollfd_connfd = (_epollfd_connfd*)param; 189 //int epollfd = ptr_epollfd_connfd->epollfd; 190 //获取客户连接socket 191 int conn_sock = ptr_epollfd_connfd->connfd; 192 193 struct epoll_event ev, events[2]; 194 ev.events = EPOLLIN | EPOLLET; 195 ev.data.fd = conn_sock; 196 //针对客户链接的新epollfd 197 int epollfd = tyhp_epoll_create(2); 198 tyhp_epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev); 199 int nfds = 0; 200 201 pthread_t tid = pthread_self(); 202 printf("NO.%u thread runs now !!!\n", (unsigned int)tid); 203 204 //Nginx默认http请求包大小为1M,所以我也分配1M缓存来存http请求包 205 char *buff = (char*)tyhp_malloc(ONEMEGA); 206 bzero(buff, ONEMEGA); 207 208 //关闭connfd的Nagle算法 209 tyhp_set_off_tcp_nagle(conn_sock); 210 //设置接收超时时间为60秒 211 tyhp_set_recv_timeo(conn_sock, 60, 0); 212 //设置发送超时时间为120秒 213 //tyhp_set_snd_timeo(connfd, 120, 0); 214 begin: 215 int32_t nread = 0, n = 0; 216 for(;;) 217 { 218 if((n = read(conn_sock, buff+nread, ONEMEGA-1)) > 0) 219 nread += n; 220 else if(0 == n) 221 break; 222 else if(-1 == n && errno == EINTR) 223 continue; 224 else if(-1 == n && errno == EAGAIN) 225 break; 226 else if(-1 == n && errno == EWOULDBLOCK) 227 { 228 perror("socket read timeout"); 229 goto out; 230 } 231 else 232 { 233 perror("read http request error"); 234 tyhp_free(buff); 235 break; 236 } 237 238 } 239 240 if(0 != nread) 241 { 242 string str_http_request(buff, buff + nread); 243 244 //do_something(str_http_request); 245 if(!tyhp_parse_http_request(str_http_request, phttphdr)) 246 { 247 perror("tyhp_parse_http_request: parse str_http_request failed"); 248 goto out; 249 } 250 cout<<"解析出来的http请求包:"<<endl; 251 tyhp_print_http_header(phttphdr); 252 253 string out; 254 int http_codes = tyhp_do_http_header(phttphdr, out); 255 256 /****** debug *****/ 257 cout<<"http响应包:"<<endl<<out<<endl; 258 259 char *out_buf = (char *)tyhp_malloc(out.size()); 260 if(out_buf == NULL) 261 goto out; 262 int i; 263 for(i = 0; i != out.size(); ++i) 264 out_buf[i] = out[i]; 265 out_buf[i] = '\0'; 266 int nwrite = 0; n = 0; 267 if( http_codes == TYHP_BADREQUEST || 268 http_codes == TYHP_NOIMPLEMENTED || 269 http_codes == TYHP_NOTFOUND || 270 (http_codes == TYHP_OK && phttphdr->method == "HEAD")) 271 { 272 while((n = write(conn_sock, out_buf + nwrite, i)) != 0) 273 { 274 if(n == -1 && errno == EINTR) 275 continue; 276 else 277 goto out; 278 nwrite += n; 279 } 280 } 281 if(http_codes == TYHP_OK) 282 { 283 if(phttphdr->method == "GET") 284 { 285 while((n = write(conn_sock, out_buf + nwrite, i)) != 0) 286 { 287 cout<<n<<endl; 288 if(n == -1 && errno == EINTR) 289 continue; 290 else 291 break; 292 nwrite += n; 293 } 294 string real_url = tyhp_make_real_url(phttphdr->url); 295 int fd = open(real_url.c_str(), O_RDONLY); 296 int file_size = tyhp_get_file_length(real_url.c_str()); 297 cout<<"file size "<<file_size<<endl; 298 int nwrite = 0; 299 cout<<"sendfile : "<<real_url.c_str()<<endl; 300 again: 301 if((sendfile(conn_sock, fd, (off_t*)&nwrite, file_size)) < 0) 302 perror("sendfile"); 303 if(nwrite < file_size) 304 goto again; 305 cout<<"sendfile ok:"<<nwrite<<endl; 306 } 307 } 308 free(out_buf); 309 //超时4分钟 310 nfds = tyhp_epoll_wait(epollfd, events, 2, TIMEOUT); 311 if(0 == nfds)//timeout 312 goto out; 313 for(int i = 0; i < nfds; ++i) 314 { 315 if(events[i].data.fd == conn_sock) 316 goto begin; 317 else 318 goto out; 319 } 320 } 321 322 out: 323 tyhp_free_http_header(phttphdr); 324 close(conn_sock); 325 tyhp_thread_num_minus1(); 326 printf("NO.%u thread ends now ~~~\n", (unsigned int)tid); 327 } 328 329 /* 330 *函数作用:tyhp_thread_num原子加1 331 *函数参数:无 332 *函数返回值: 无 333 */ 334 void tyhp_thread_num_add1() 335 { 336 pthread_mutex_lock(&tyhp_thread_num_mutex); 337 ++tyhp_thread_num; 338 pthread_mutex_unlock(&tyhp_thread_num_mutex); 339 } 340 /* 341 *函数作用:tyhp_thread_num原子减1 342 *函数参数:无 343 *函数返回值: 无 344 */ 345 void tyhp_thread_num_minus1() 346 { 347 pthread_mutex_lock(&tyhp_thread_num_mutex); 348 --tyhp_thread_num; 349 pthread_mutex_unlock(&tyhp_thread_num_mutex); 350 } 351 /* 352 *函数作用:tyhp_thread_num原子读 353 *函数参数:无 354 *函数返回值: tyhp_thread_num当前值 355 */ 356 int32_t tyhp_thread_num_read(); 357 /*****************************************************************************************/ 358 359 /******************************** tyhp_http_header_t处理函数 *********************************/ 360 /* 361 *函数作用:根据解析下来的tyhp_http_header_t来处理客户的请求 362 *函数参数: phttphdr指向要处理的tyhp_http_header_t 363 out保存了处理的结果,即http响应包 364 *函数返回值: HTTP状态码 365 366 *目前支持的请求首部: 367 *目前支持的响应首部:Date,Content-Base,Content-Length,Content-Location 368 Last-Modified,Public,Server 369 */ 370 int tyhp_do_http_header(tyhp_http_header_t *phttphdr, string& out) 371 { 372 char status_line[256] = {0}; 373 string crlf("\r\n"); 374 string server("Server: tinyhttp\r\n"); 375 string Public("Public: GET, HEAD\r\n"); 376 string content_base = "Content-Base: " + tyhp_domain + crlf; 377 string date = "Date:" + tyhp_time_gmt() + crlf; 378 379 string content_length("Content-Length: "); 380 string content_location("Content-Location: "); 381 string last_modified("Last-Modified: "); 382 //string body(""); 383 384 if(phttphdr == NULL) 385 { 386 snprintf(status_line, sizeof(status_line), "HTTP/1.1 %d %s\r\n", 387 TYHP_BADREQUEST, tyhp_get_state_by_codes(TYHP_BADREQUEST)); 388 out = status_line + crlf; 389 return TYHP_BADREQUEST; 390 } 391 392 string method = phttphdr->method; 393 string real_url = tyhp_make_real_url(phttphdr->url); 394 string version = phttphdr->version; 395 if(method == "GET" || method == "HEAD") 396 { 397 if(tyhp_is_file_existed(real_url.c_str()) == -1) 398 { 399 snprintf(status_line, sizeof(status_line), "HTTP/1.1 %d %s\r\n", 400 TYHP_NOTFOUND, tyhp_get_state_by_codes(TYHP_NOTFOUND)); 401 out += (status_line + server + date + crlf); 402 return TYHP_NOTFOUND; 403 } 404 else 405 { 406 int len = tyhp_get_file_length(real_url.c_str()); 407 snprintf(status_line, sizeof(status_line), "HTTP/1.1 %d %s\r\n", 408 TYHP_OK, tyhp_get_state_by_codes(TYHP_OK)); 409 out += status_line; 410 snprintf(status_line, sizeof(status_line), "%d\r\n", len); 411 out += content_length + status_line; 412 out += server + content_base + date; 413 out += last_modified + tyhp_get_file_modified_time(real_url.c_str()) + crlf + crlf; 414 } 415 } 416 else if(method == "PUT") 417 { 418 snprintf(status_line, sizeof(status_line), "HTTP/1.1 %d %s\r\n", 419 TYHP_NOIMPLEMENTED, tyhp_get_state_by_codes(TYHP_NOIMPLEMENTED)); 420 out += status_line + server + Public + date + crlf; 421 return TYHP_NOIMPLEMENTED; 422 } 423 else if(method == "DELETE") 424 { 425 snprintf(status_line, sizeof(status_line), "HTTP/1.1 %d %s\r\n", 426 TYHP_NOIMPLEMENTED, tyhp_get_state_by_codes(TYHP_NOIMPLEMENTED)); 427 out += status_line + server + Public + date + crlf; 428 return TYHP_NOIMPLEMENTED; 429 } 430 else if(method == "POST") 431 { 432 snprintf(status_line, sizeof(status_line), "HTTP/1.1 %d %s\r\n", 433 TYHP_NOIMPLEMENTED, tyhp_get_state_by_codes(TYHP_NOIMPLEMENTED)); 434 out += status_line + server + Public + date + crlf; 435 return TYHP_NOIMPLEMENTED; 436 } 437 else 438 { 439 snprintf(status_line, sizeof(status_line), "HTTP/1.1 %d %s\r\n", 440 TYHP_BADREQUEST, tyhp_get_state_by_codes(TYHP_BADREQUEST)); 441 out = status_line + crlf; 442 return TYHP_BADREQUEST; 443 } 444 445 return TYHP_OK; 446 } 447 /* 448 #define TYHP_CONTINUE 100 //收到了请求的起始部分,客户端应该继续请求 449 450 #define TYHP_OK 200 //服务器已经成功处理请求 451 #define TYHP_ACCEPTED 202 //请求已接受,服务器尚未处理 452 453 #define TYHP_MOVED 301 //请求的URL已移走,响应应该包含Location URL 454 #define TYHP_FOUND 302 //请求的URL临时移走,响应应该包含Location URL 455 #define TYHP_SEEOTHER 303 //告诉客户端应该用另一个URL获取资源,响应应该包含Location URL 456 #define TYHP_NOTMODIFIED 304 //资源未发生变化 457 458 #define TYHP_BADREQUEST 400 //客户端发送了一条异常请求 459 #define TYHP_FORBIDDEN 403 //服务器拒绝请求 460 #define TYHP_NOTFOUND 404 //URL未找到 461 462 #define TYHP_ERROR 500 //服务器出错 463 #define TYHP_NOIMPLEMENTED 501 //服务器不支持当前请求所需要的某个功能 464 #define TYHP_BADGATEWAY 502 //作为代理或网关使用的服务器遇到了来自响应链中上游的无效响应 465 #define TYHP_SRVUNAVAILABLE 503 //服务器目前无法提供请求服务,过一段时间后可以恢复 466 467 /* 468 *函数作用:通过HTTP状态码返回友好语句 469 *函数参数:HTTP状态码 470 *函数返回值: 相应的语句 471 */ 472 char *tyhp_get_state_by_codes(int http_codes) 473 { 474 switch (http_codes) 475 { 476 case TYHP_OK: 477 return tyhp_ok; 478 case TYHP_BADREQUEST: 479 return tyhp_badrequest; 480 case TYHP_FORBIDDEN: 481 return tyhp_forbidden; 482 case TYHP_NOTFOUND: 483 return tyhp_notfound; 484 case TYHP_NOIMPLEMENTED: 485 return tyhp_noimplemented; 486 default: 487 break; 488 } 489 490 return NULL; 491 } 492 /*****************************************************************************************/
1 /* 2 *Author Zou Xiao hang 3 *Email [email protected] 4 *File Name parse.cpp 5 *Date 2013/10/05 6 */ 7 #include <stdlib.h> 8 #include <stdio.h> 9 #include <string> 10 #include <map> 11 #include <utility> 12 #include <sstream> 13 #include <ctype.h> 14 #include <iostream> 15 16 using namespace std; 17 18 typedef map<string, string> tyhp_header; 19 20 //#define string::npos REQUEST_END 21 #define make_tyhp_header(key, value) \ 22 make_pair((key), (value)) 23 24 //保存从http request解析下来的值 25 typedef struct _tyhp_http_header_t 26 { 27 string method; 28 string url; 29 string version; 30 31 tyhp_header header; 32 33 string body; 34 }tyhp_http_header_t; 35 36 /* 37 *函数作用:打印tyhp_http_header_t里的header 38 *函数参数:tyhp_header 的const 引用 39 *函数返回值: 无 40 */ 41 void tyhp_print_http_header_header(const tyhp_header& head); 42 /* 43 *函数作用:打印tyhp_http_header_t 44 *函数参数:tyhp_http_header_t指针 45 *函数返回值: 无 46 */ 47 void tyhp_print_http_header(tyhp_http_header_t *phttphdr); 48 49 /* 50 *函数作用:分配内存给tyhp_http_header_t 51 *函数参数:无 52 *函数返回值: NULL表示分配失败,其他值表示成功 53 */ 54 tyhp_http_header_t *tyhp_alloc_http_header(); 55 /* 56 *函数作用:回收分配给tyhp_http_header_t的内存 57 *函数参数:tyhp_http_header_t指针 58 *函数返回值: 无 59 */ 60 void tyhp_free_http_header(tyhp_http_header_t *phttphdr); 61 /* 62 *函数作用:解析http_request 63 *函数参数:http_request为待解析的值,phttphdr保存了解析下来的值 64 *函数返回值: true表示解析成功,false表示解析失败 65 */ 66 bool tyhp_parse_http_request(const string& http_request, tyhp_http_header_t *phttphdr); 67 /* 68 *函数作用:根据key的值在phttphdr所指向的tyhp_http_header_t中查找相对应的值 69 *函数参数:key为关键字,header 70 *函数返回值: -返回空值表示查找失败,否则返回相应的值 71 */ 72 string tyhp_get_value_from_http_header(const string& key, const tyhp_header& header);
1 /* 2 *Author Zou Xiao hang 3 *Email [email protected] 4 *File Name parse.h 5 *Date 2013/10/05 6 */ 7 #include "parse.h" 8 9 /* 10 *函数作用:打印tyhp_http_header_t里的header 11 *函数参数:tyhp_header 的const 引用 12 *函数返回值: 无 13 */ 14 void tyhp_print_http_header_header(const tyhp_header& head) 15 { 16 if(!head.empty()) 17 { 18 tyhp_header::const_iterator cit = head.begin(); 19 while(cit != head.end()) 20 { 21 cout<<cit->first<<":"<<cit->second<<endl; 22 ++cit; 23 } 24 } 25 } 26 /* 27 *函数作用:打印tyhp_http_header_t 28 *函数参数:tyhp_http_header_t指针 29 *函数返回值: 无 30 */ 31 void tyhp_print_http_header(tyhp_http_header_t *phttphdr) 32 { 33 if(NULL == phttphdr) 34 { 35 perror("phttphdr == NULL"); 36 return ; 37 } 38 39 cout<<phttphdr->method<<" "<<phttphdr->url<<" "<<phttphdr->version<<endl; 40 tyhp_print_http_header_header(phttphdr->header); 41 cout<<endl<<phttphdr->body<<endl; 42 } 43 44 45 /* 46 *函数作用:分配内存给tyhp_http_header_t 47 *函数参数:无 48 *函数返回值: NULL表示分配失败,其他值表示成功 49 */ 50 tyhp_http_header_t *tyhp_alloc_http_header() 51 { 52 tyhp_http_header_t *phttphdr = (tyhp_http_header_t *)new tyhp_http_header_t; 53 if(phttphdr == NULL) 54 { 55 perror("tyhp_alloc_http_header"); 56 exit(-1); 57 } 58 return phttphdr; 59 } 60 61 /* 62 *函数作用:回收分配给tyhp_http_header_t的内存 63 *函数参数:tyhp_http_header_t指针 64 *函数返回值: 无 65 */ 66 void tyhp_free_http_header(tyhp_http_header_t *phttphdr) 67 { 68 if(phttphdr == NULL) 69 return ; 70 delete phttphdr; 71 } 72 73 /* 74 *函数作用:解析http_request 75 *函数参数:http_request为待解析的值,phttphdr保存了解析下来的值 76 *函数返回值: true表示解析成功,false表示解析失败 77 */ 78 bool tyhp_parse_http_request(const string& http_request, tyhp_http_header_t *phttphdr) 79 { 80 if(http_request.empty()) 81 { 82 perror("tyhp_parse_http_request: http_request is empty"); 83 return false; 84 } 85 if(phttphdr == NULL) 86 { 87 perror("tyhp_parse_http_request: phttphdr is NULL"); 88 return false; 89 } 90 91 string crlf("\r\n"), crlfcrlf("\r\n\r\n"); 92 int prev = 0, next = 0; 93 94 //解析http请求包的起始行 95 if((next = http_request.find(crlf, prev)) != string::npos) 96 { 97 string first_line(http_request.substr(prev, next - prev)); 98 prev = next; 99 stringstream sstream(first_line); 100 sstream >> (phttphdr->method); 101 sstream >> (phttphdr->url); 102 sstream >> (phttphdr->version); 103 } 104 else 105 { 106 perror("tyhp_parse_http_request: http_request has not a \\r\\n"); 107 return false; 108 } 109 110 //查找"\r\n\r\n"的位置 111 int pos_crlfcrlf = http_request.find(crlfcrlf, prev); 112 if(pos_crlfcrlf == string::npos) 113 { 114 perror("tyhp_parse_http_request: http_request has not a \"\r\n\r\n\""); 115 return false; 116 } 117 118 //解析首部行 119 string buff, key, value; 120 while(1) 121 { 122 next = http_request.find(crlf, prev+2); 123 124 //如果找到的next不超过"\r\n\r\n"的位置 125 if(next <= pos_crlfcrlf) 126 { 127 //buff保存了一行 128 buff = http_request.substr(prev + 2, next - prev - 2); 129 int end = 0; 130 //跳过前置空白符,到达首部关键字的起始位置 131 for(; isblank(buff[end]); ++end) 132 ; 133 int beg = end; 134 //到达首部关键字的结束位置 135 for(; buff[end] != ':' && !isblank(buff[end]); ++end) 136 ; 137 key = buff.substr(beg, end - beg); 138 //跳至首部值的起始位置 139 for(; (!isalpha(buff[end]) && !isdigit(buff[end])); ++end) 140 ; 141 beg = end; 142 //到达首部值的结束位置 143 for(; next != end; ++end) 144 ; 145 value = buff.substr(beg, end - beg); 146 phttphdr->header.insert(make_tyhp_header(key, value)); 147 148 prev = next; 149 } 150 else 151 { 152 break; 153 } 154 } 155 156 //获取http请求包的实体值(一般情况下不存在) 157 phttphdr->body = http_request.substr(pos_crlfcrlf + 4, http_request.size() - pos_crlfcrlf - 4); 158 159 return true; 160 } 161 162 /* 163 *函数作用:根据key的值在phttphdr所指向的tyhp_http_header_t中查找相对应的值 164 *函数参数:key为关键字,header 165 *函数返回值: -返回空值表示查找失败,否则返回相应的值 166 */ 167 string tyhp_get_value_from_http_header(const string& key, const tyhp_header& header) 168 { 169 if(header.empty()) 170 return ""; 171 tyhp_header::const_iterator cit = header.find(key); 172 if(cit == header.end()) 173 return ""; 174 return (*cit).second; 175 }
1 /* 2 *Author Zou Xiao hang 3 *Email [email protected] 4 *File Name utility.h 5 *Date 2013/10/05 6 */ 7 #ifndef _UTILITY_H_ 8 #define _UTILITY_H_ 9 10 #include <sys/socket.h> 11 #include <sys/types.h> 12 #include <stdlib.h> 13 #include <stdio.h> 14 #include <netdb.h> 15 #include <sys/epoll.h> 16 #include <strings.h> 17 #include <string> 18 #include <errno.h> 19 #include <unistd.h> 20 #include <fcntl.h> 21 #include <utility> 22 #include <fstream> 23 #include <sstream> 24 #include <map> 25 #include <iostream> 26 #include <string.h> 27 #include <pthread.h> 28 #include <netinet/tcp.h> 29 #include <time.h> 30 #include <sys/stat.h> 31 32 using namespace std; 33 34 extern string tyhp_docroot; 35 #define TYHP_DOCROOT 1 36 extern string tyhp_domain; 37 #define TYHP_DOMAIN 2 38 39 /*********************************** 项目实用工具函数 *******************************************/ 40 /* 41 *函数作用:得到系统时间 42 *函数参数:无 43 *函数返回值: 系统时间 例如:Fri, 22 May 2009 06:07:21 GMT 44 */ 45 string tyhp_time_gmt(); 46 /* 47 *函数作用:根据http请求包中的url和配置文件中的docroot配置选项构造真正的url 48 *函数参数:url 49 *函数返回值: 真正的url(绝对路径) 50 */ 51 string tyhp_make_real_url(const string& url); 52 /* 53 *函数作用:测试文件是否存在 54 *函数参数:path为绝对路径+文件名 55 *函数返回值: -1表示文件不存在,其他值表示文件存在 56 */ 57 inline int tyhp_is_file_existed(const char *path) 58 { 59 int ret = open(path, O_RDONLY | O_EXCL); 60 close(ret); 61 return ret; 62 } 63 /* 64 *函数作用:获得文件长度 65 *函数参数:path为绝对路径+文件名 66 *函数返回值: 文件长度 67 */ 68 int tyhp_get_file_length(const char *path); 69 /* 70 *函数作用:获得文件最后修改时间 71 *函数参数:path为绝对路径+文件名 72 *函数返回值: 文件最后修改时间 73 */ 74 string tyhp_get_file_modified_time(const char *path); 75 /* 76 *函数作用:初始化全局变量tyhp_config_keyword_map,必须在使用tyhp_config_keyword_map前调用此函数 77 *函数参数:无 78 *函数返回值: 无 79 */ 80 void tyhp_init_config_keyword_map(); 81 /* 82 *函数作用:解析配置文件 83 *函数参数:path为绝对路径+文件名 84 *函数返回值: -1表示解析失败,0代表解析成功 85 */ 86 int tyhp_parse_config(const char *path); 87 /* 88 *函数作用:设置文件描述符为非阻塞模式 89 *函数参数:要设置的描述符 90 *函数返回值: 无 91 */ 92 void tyhp_set_nonblocking(int fd); 93 /* 94 *函数作用:设置套接字SO_REUSEADDR选项 95 *函数参数:要设置的套接字 96 *函数返回值: 无 97 */ 98 void tyhp_set_reuse_addr(int sockfd); 99 /* 100 *函数作用:开启套接字TCP_NODELAY选项,关闭nagle算法 101 *函数参数:要设置的套接字 102 *函数返回值: 无 103 */ 104 void tyhp_set_off_tcp_nagle(int sockfd); 105 /* 106 *函数作用:关闭套接字TCP_NODELAY选项,开启nagle算法 107 *函数参数:要设置的套接字 108 *函数返回值: 无 109 */ 110 void tyhp_set_on_tcp_nagle(int sockfd); 111 /* 112 *函数作用:开启套接字TCP_CORK选项 113 *函数参数:要设置的套接字 114 *函数返回值: 无 115 */ 116 void tyhp_set_on_tcp_cork(int sockfd); 117 /* 118 *函数作用:关闭套接字TCP_CORK选项 119 *函数参数:要设置的套接字 120 *函数返回值: 无 121 */ 122 void tyhp_set_off_tcp_cork(int sockfd); 123 /* 124 *函数作用:设置套接字SO_RCVTIMEO选项,接收超时 125 *函数参数:sockfd要设置的套接字, sec秒, usec毫秒 126 *函数返回值: 无 127 */ 128 void tyhp_set_recv_timeo(int sockfd, int sec, int usec); 129 /* 130 *函数作用:设置套接字SO_SNDTIMEO选项,发送超时 131 *函数参数:sockfd要设置的套接字, sec秒, usec毫秒 132 *函数返回值: 无 133 */ 134 void tyhp_set_snd_timeo(int sockfd, int sec, int usec); 135 /******************************************************************************************/ 136 137 /*********************************** 系统函数的包裹函数 *******************************************/ 138 int tyhp_socket(int domain, int type, int protocol); 139 void tyhp_listen(int sockfd, int backlog); 140 void tyhp_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 141 int tyhp_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 142 struct servent* tyhp_getservbyname(const char *name, const char *proto); 143 int tyhp_epoll_create(int size); 144 void tyhp_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 145 int tyhp_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); 146 147 void *tyhp_calloc(size_t nmemb, size_t size); 148 void *tyhp_malloc(size_t size); 149 void tyhp_free(void *ptr); 150 /******************************************************************************************/ 151 #endif
1 /* 2 *Author Zou Xiao hang 3 *Email [email protected] 4 *File Name utility.cpp 5 *Date 2013/10/05 6 */ 7 #include "utility.h" 8 9 //存储解析文件的键值对,例如key=docroot value=/home/zxh/desktop/code/webserver 10 map<string, int> tyhp_config_keyword_map; 11 12 /*********************************** 项目实用工具函数 *******************************************/ 13 /* 14 *函数作用:得到系统时间 15 *函数参数:无 16 *函数返回值: 系统时间 例如:Fri, 22 May 2009 06:07:21 GMT 17 */ 18 string tyhp_time_gmt() 19 { 20 time_t now; 21 struct tm *time_now; 22 string str_time; 23 24 time(&now); 25 time_now = localtime(&now); 26 27 switch(time_now->tm_wday) 28 { 29 case 0: 30 str_time += "Sun, "; 31 break; 32 case 1: 33 str_time += "Mon, "; 34 break; 35 case 2: 36 str_time += "Tue, "; 37 break; 38 case 3: 39 str_time += "Wed, "; 40 break; 41 case 4: 42 str_time += "Thu, "; 43 break; 44 case 5: 45 str_time += "Fri, "; 46 break; 47 case 6: 48 str_time += "Sat, "; 49 break; 50 } 51 char buf[16]; 52 snprintf(buf, sizeof(buf), "%d ", time_now->tm_mday); 53 str_time += string(buf); 54 switch(time_now->tm_mon) 55 { 56 case 0: 57 str_time += "Jan "; 58 break; 59 case 1: 60 str_time += "Feb "; 61 break; 62 case 2: 63 str_time += "Mar "; 64 break; 65 case 3: 66 str_time += "Apr "; 67 break; 68 case 4: 69 str_time += "May "; 70 break; 71 case 5: 72 str_time += "Jun "; 73 break; 74 case 6: 75 str_time += "Jul "; 76 break; 77 case 7: 78 str_time += "Aug "; 79 break; 80 case 8: 81 str_time += "Sep "; 82 break; 83 case 9: 84 str_time += "Oct "; 85 break; 86 case 10: 87 str_time += "Nov "; 88 break; 89 case 11: 90 str_time += "Dec "; 91 break; 92 } 93 snprintf(buf, sizeof(buf), "%d", time_now->tm_year + 1900); 94 str_time += string(buf); 95 snprintf(buf, sizeof(buf), " %d:%d:%d ", time_now->tm_hour, time_now->tm_min, time_now->tm_sec); 96 str_time += string(buf); 97 98 str_time += "GMT"; 99 100 return str_time; 101 } 102 /* 103 *函数作用:根据http请求包中的url和配置文件中的docroot配置选项构造真正的url 104 *函数参数:url 105 *函数返回值: 真正的url(绝对路径) 106 */ 107 string tyhp_make_real_url(const string& url) 108 { 109 string real_url, url2; 110 111 int n = 0; 112 113 if((n = url.find(tyhp_domain, 0)) != string::npos)//url中包含域名,要将其删去 114 url2 = url.substr(tyhp_domain.size(), url.size() - tyhp_domain.size()); 115 else 116 url2 = url; 117 118 if(tyhp_docroot[tyhp_docroot.size() - 1] == '/')//配置项docroot末尾有'/' 119 { 120 if(url2[0] == '/') 121 real_url = tyhp_docroot + url2.erase(0, 1); 122 else 123 real_url = tyhp_docroot + url2; 124 } 125 else//配置项docroot末尾没有'\' 126 { 127 if(url2[0] == '/') 128 real_url = tyhp_docroot + url2; 129 else 130 real_url = tyhp_docroot + '/' + url2; 131 } 132 133 return real_url; 134 } 135 /* 136 *函数作用:获得文件长度 137 *函数参数:path为绝对路径+文件名 138 *函数返回值: 文件长度 139 */ 140 int tyhp_get_file_length(const char *path) 141 { 142 struct stat buf; 143 int ret = stat(path, &buf); 144 if(ret == -1) 145 { 146 perror("tyhp_get_file_length"); 147 exit(-1); 148 } 149 return (int)buf.st_size; 150 } 151 /* 152 *函数作用:获得文件最后修改时间 153 *函数参数:path为绝对路径+文件名 154 *函数返回值: 文件最后修改时间 155 */ 156 string tyhp_get_file_modified_time(const char *path) 157 { 158 struct stat buf; 159 int ret = stat(path, &buf); 160 if(ret == -1) 161 { 162 perror("tyhp_get_file_length"); 163 exit(-1); 164 } 165 char array[32] = {0}; 166 snprintf(array, sizeof(array), "%s", ctime(&buf.st_mtime)); 167 return string(array, array + strlen(array)); 168 } 169 /* 170 *函数作用:初始化全局变量tyhp_config_keyword_map,必须在使用tyhp_config_keyword_map前调用此函数 171 *函数参数:无 172 *函数返回值: 无 173 */ 174 void tyhp_init_config_keyword_map() 175 { 176 tyhp_config_keyword_map.insert(make_pair("docroot", TYHP_DOCROOT)); 177 tyhp_config_keyword_map.insert(make_pair("domain", TYHP_DOMAIN)); 178 } 179 /* 180 *函数作用:解析配置文件 181 *函数参数:path为绝对路径+文件名 182 *函数返回值: -1表示解析失败,0代表解析成功 183 */ 184 int tyhp_parse_config(const char *path) 185 { 186 tyhp_init_config_keyword_map(); 187 int ret = 0; 188 fstream infile(path, fstream::in); 189 string line, word; 190 if(!infile) 191 { 192 printf("%s can't open\n", path); 193 infile.close(); 194 return -1; 195 } 196 while(getline(infile, line)) 197 { 198 stringstream stream(line); 199 stream >> word;//keyword 200 map<string, int>::const_iterator cit= tyhp_config_keyword_map.find(word); 201 if(cit == tyhp_config_keyword_map.end()) 202 { 203 printf("can't find keyword\n"); 204 infile.close(); 205 return -1; 206 } 207 switch (cit->second) 208 { 209 case TYHP_DOCROOT: 210 stream >> tyhp_docroot; 211 break; 212 case TYHP_DOMAIN: 213 stream >> tyhp_domain; 214 break; 215 default : 216 infile.close(); 217 return -1; 218 } 219 } 220 infile.close(); 221 return 0; 222 } 223 /* 224 *函数作用:设置文件描述符为非阻塞模式 225 *函数参数:要设置的描述符 226 *函数返回值: 无 227 */ 228 void tyhp_set_nonblocking(int fd) 229 { 230 int flags = fcntl(fd, F_GETFL, 0); 231 if(flags < 0) 232 { 233 perror("fcntl: F_GETFL"); 234 exit(-1); 235 } 236 flags |= O_NONBLOCK; 237 int ret = fcntl(fd, F_SETFL, flags); 238 if(ret < 0) 239 { 240 perror("fcntl"); 241 exit(-1); 242 } 243 } 244 /* 245 *函数作用:设置套接字SO_REUSEADDR选项 246 *函数参数:要设置的套接字 247 *函数返回值: 无 248 */ 249 void tyhp_set_reuse_addr(int sockfd) 250 { 251 int on = 1; 252 int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 253 if(ret == -1) 254 { 255 perror("setsockopt: SO_REUSEADDR"); 256 exit(-1); 257 } 258 } 259 /* 260 *函数作用:开启套接字TCP_NODELAY选项,关闭nagle算法 261 *函数参数:要设置的套接字 262 *函数返回值: 无 263 */ 264 void tyhp_set_off_tcp_nagle(int sockfd) 265 { 266 int on = 1; 267 int ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); 268 if(ret == -1) 269 { 270 perror("setsockopt: TCP_NODELAY ON"); 271 exit(-1); 272 } 273 } 274 /* 275 *函数作用:关闭套接字TCP_NODELAY选项,开启nagle算法 276 *函数参数:要设置的套接字 277 *函数返回值: 无 278 */ 279 void tyhp_set_on_tcp_nagle(int sockfd) 280 { 281 int off = 0; 282 int ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off)); 283 if(ret == -1) 284 { 285 perror("setsockopt: TCP_NODELAY OFF"); 286 exit(-1); 287 } 288 } 289 /* 290 *函数作用:开启套接字TCP_CORK选项 291 *函数参数:要设置的套接字 292 *函数返回值: 无 293 */ 294 void tyhp_set_on_tcp_cork(int sockfd) 295 { 296 int on = 1; 297 int ret = setsockopt(sockfd, SOL_TCP, TCP_CORK, &on, sizeof(on)); 298 if(ret == -1) 299 { 300 perror("setsockopt: TCP_CORK ON"); 301 exit(-1); 302 } 303 } 304 /* 305 *函数作用:关闭套接字TCP_CORK选项 306 *函数参数:要设置的套接字 307 *函数返回值: 无 308 */ 309 void tyhp_set_off_tcp_cork(int sockfd) 310 { 311 int off = 0; 312 int ret = setsockopt(sockfd, SOL_TCP, TCP_CORK, &off, sizeof(off)); 313 if(ret == -1) 314 { 315 perror("setsockopt: TCP_CORK OFF"); 316 exit(-1); 317 } 318 } 319 /* 320 *函数作用:设置套接字SO_RCVTIMEO选项,接收超时 321 *函数参数:sockfd要设置的套接字, sec秒, usec毫秒 322 *函数返回值: 无 323 */ 324 void tyhp_set_recv_timeo(int sockfd, int sec, int usec) 325 { 326 struct timeval time= {sec, usec}; 327 int ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &time, sizeof(time)); 328 if(ret == -1) 329 { 330 perror("setsockopt: SO_RCVTIMEO"); 331 exit(-1); 332 } 333 } 334 /* 335 *函数作用:设置套接字SO_SNDTIMEO选项,发送超时 336 *函数参数:sockfd要设置的套接字, sec秒, usec毫秒 337 *函数返回值: 无 338 */ 339 void tyhp_set_snd_timeo(int sockfd, int sec, int usec) 340 { 341 struct timeval time= {sec, usec}; 342 int ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &time, sizeof(time)); 343 if(ret == -1) 344 { 345 perror("setsockopt: SO_SNDTIMEO"); 346 exit(-1); 347 } 348 } 349 /******************************************************************************************/ 350 351 /*********************************** 系统函数的包裹函数 *******************************************/ 352 int tyhp_socket(int domain, int type, int protocol) 353 { 354 int listen_fd; 355 if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 356 { 357 perror("socket"); 358 exit(-1); 359 } 360 return listen_fd; 361 } 362 void tyhp_listen(int sockfd, int backlog) 363 { 364 if(listen(sockfd, backlog) == -1) 365 { 366 perror("listen"); 367 exit(-1); 368 } 369 } 370 void tyhp_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) 371 { 372 if(bind(sockfd, addr, addrlen) == -1) 373 { 374 perror("bind"); 375 exit(-1); 376 } 377 } 378 int tyhp_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) 379 { 380 int ret_fd = 0; 381 for(;;) 382 { 383 ret_fd = accept(sockfd, addr, addrlen); 384 if(ret_fd > 0) 385 break; 386 else if(ret_fd == -1) 387 { 388 //由于我们把监听套接字设置为了非阻塞模式 389 if(errno != EAGAIN && errno != EPROTO && 390 errno != EINTR && errno != ECONNABORTED) 391 { 392 perror("accept"); 393 exit(-1); 394 } 395 } 396 else 397 continue; 398 } 399 return ret_fd; 400 } 401 struct servent* tyhp_getservbyname(const char *name, const char *proto) 402 { 403 struct servent *pservent; 404 if((pservent = getservbyname(name, proto)) == NULL) 405 { 406 perror("getservbyname"); 407 exit(-1); 408 } 409 return pservent; 410 } 411 int tyhp_epoll_create(int size) 412 { 413 int epollfd; 414 epollfd = epoll_create(size); 415 if(-1 == epollfd) 416 { 417 perror("epoll_create"); 418 exit(-1); 419 } 420 return epollfd; 421 } 422 void tyhp_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 423 { 424 if(epoll_ctl(epfd, op, fd, event) == -1) 425 { 426 perror("epoll_ctl"); 427 exit(-1); 428 } 429 } 430 int tyhp_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) 431 { 432 again: 433 int nfds = epoll_wait(epfd, events, maxevents, timeout); 434 if(nfds == -1) //&& errno != EINTR) 435 { 436 if(errno != EINTR) 437 { 438 perror("epoll_wait"); 439 exit(-1); 440 } 441 else 442 goto again; 443 } 444 return nfds; 445 } 446 447 void *tyhp_calloc(size_t nmemb, size_t size) 448 { 449 void *ptr = calloc(nmemb, size); 450 if(NULL == ptr) 451 { 452 perror("tyhp_calloc"); 453 exit(-1); 454 } 455 return ptr; 456 } 457 void *tyhp_malloc(size_t size) 458 { 459 void *ptr = malloc(size); 460 if(NULL == ptr) 461 { 462 perror("tyhp_malloc"); 463 exit(-1); 464 } 465 return ptr; 466 } 467 void tyhp_free(void *ptr) 468 { 469 free(ptr); 470 } 471 /******************************************************************************************/