• EPOLL单线程版本 基于reactor 的 httpserver文件下载 支持多个客户端同时处理


    之前写了一个httpserver的问价下载服务器    如果有多个客户端请求过来只能串行处理必须得等当前的操作完成之后才会处理   

    另外还存在 文件大的时候 会出错 处理不了  原因就是 sendfile是在一个while循环中处理的  

    当调用send失败返回-1之后 就  结束了   而一般来讲  send的时候发送的数据超过内核中的send buffer的大小的时候  就会  失败了  

    这个时候 必须 要保存下来当前文件的已发送的字节数 以及当前文件的偏移指针 等下一次 EPOLLOUT事件的时候再次 发送给客户端  

    目前已经实现了这个功能 采用的是单线程版本的reactor模式  

    支持 多个客户端同时下载文件 

    还存在bug 但是  功能是有了  

    1. #include
    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. #include
    17. #include
    18. #include
    19. #include
    20. #include
    21. #include
    22. #include
    23. #include
    24. #include
    25. #include
    26. typedef int (*READ_CB)(void *user_data);
    27. typedef int (*WRITE_CB)(void *user_data);
    28. typedef int (*ACCEPT_CB)(int epoll_fd,int fd,void *user_data);
    29. #define READ_ONETIME 100
    30. #define MAX_SESSIONS 1024
    31. typedef struct
    32. {
    33. int fd;
    34. int file_fd = -1;
    35. char write_buffer[1024];
    36. char read_buffer[1024];
    37. int write_offset;
    38. int read_offset;
    39. int send_file_read_len = 0;
    40. char writeable;
    41. char is_dir;
    42. char head_has_send = 0;
    43. char file_path[512]={0};
    44. int file_size = 0;
    45. READ_CB read_cb;
    46. WRITE_CB write_cb;
    47. ACCEPT_CB accept_cb;
    48. }Session;
    49. typedef struct
    50. {
    51. int epoll_fd;
    52. int server_fd;
    53. int count;
    54. Session sessions[MAX_SESSIONS];
    55. }Reactor;
    56. int create_socket(bool is_tcp,bool block_mode,const char *led_ip,int port)
    57. {
    58. #define LISTEN_BACKLOG 10
    59. int socket_fd ;
    60. const char *server_ip = led_ip;
    61. struct sockaddr_in server_addr;
    62. if(is_tcp)
    63. {
    64. if(block_mode)
    65. {
    66. socket_fd = socket(AF_INET,SOCK_STREAM,0);
    67. }
    68. else
    69. {
    70. socket_fd = socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK,0);
    71. }
    72. }
    73. else
    74. {
    75. if(block_mode)
    76. {
    77. socket_fd = socket(AF_INET,SOCK_DGRAM,0);
    78. }
    79. else
    80. {
    81. socket_fd = socket(AF_INET,SOCK_DGRAM|SOCK_NONBLOCK,0);
    82. }
    83. }
    84. int opt = 1;
    85. if (socket_fd == -1)
    86. {
    87. printf("Create socket error\n");
    88. goto ERROR;
    89. }
    90. setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));
    91. bzero(&server_addr,sizeof(server_addr));
    92. server_addr.sin_family = AF_INET;
    93. server_addr.sin_port = htons(port);
    94. inet_pton(AF_INET,server_ip,&server_addr.sin_addr);
    95. if (bind(socket_fd, (struct sockaddr *) &server_addr,sizeof(server_addr)) == -1)
    96. {
    97. printf("Bind error\n");
    98. goto ERROR;
    99. }
    100. if (listen(socket_fd, LISTEN_BACKLOG) == -1)
    101. {
    102. printf("listen error\n");
    103. goto ERROR;
    104. }
    105. return socket_fd;
    106. ERROR:
    107. if(socket_fd>0)
    108. {
    109. close(socket_fd);
    110. }
    111. return -1;
    112. }
    113. void set_nonblock(int fd)
    114. {
    115. int opts=fcntl(fd, F_GETFL);
    116. if(opts<0)
    117. {
    118. fprintf(stderr, "fcntl(sock,GETFL)\n");
    119. return ;
    120. }
    121. opts = opts|O_NONBLOCK;
    122. if(fcntl(fd,F_SETFL,opts)<0)
    123. {
    124. fprintf(stderr, "fcntl(sock,SETFL,opts)\n");
    125. return;
    126. }
    127. }
    128. int reactor_init(Reactor &rt,ACCEPT_CB accept_cb,READ_CB read_cb,WRITE_CB write_cb)
    129. {
    130. rt.epoll_fd = epoll_create(10);
    131. if(rt.epoll_fd == -1)
    132. {
    133. perror("epoll_create failed");
    134. return -1;
    135. }
    136. rt.server_fd = create_socket(true, true, "0,0,0,0", 1234);
    137. if(rt.server_fd == -1)
    138. {
    139. perror("create_socket failed");
    140. close(rt.epoll_fd);
    141. return -1;
    142. }
    143. struct epoll_event event;
    144. event.data.fd = rt.server_fd;
    145. event.events = EPOLLIN|EPOLLET|EPOLLOUT;
    146. int ret = epoll_ctl(rt.epoll_fd,EPOLL_CTL_ADD ,rt.server_fd,&event);
    147. if(ret == -1)
    148. {
    149. perror("epoll_ctl failed");
    150. close(rt.epoll_fd);
    151. close(rt.server_fd);
    152. return -1;
    153. }
    154. for(int i = 0;i
    155. {
    156. rt.sessions[i].accept_cb = accept_cb;
    157. rt.sessions[i].read_cb = read_cb;
    158. rt.sessions[i].write_cb = write_cb;
    159. }
    160. rt.count = 0;
    161. printf("Reactor init success epollfd = %d serverfd = %d\n",rt.epoll_fd,rt.server_fd);
    162. return 0;
    163. }
    164. int reactor_run(Reactor &rt)
    165. {
    166. struct epoll_event events[100];
    167. while(true)
    168. {
    169. int ready_count = epoll_wait(rt.epoll_fd, events, 100, -1);
    170. //printf("ready_count = %d\n",ready_count);
    171. for(int i = 0;i
    172. {
    173. int index = events[i].data.fd;
    174. //printf("index = %d epollfd = %d events[i].data.fd = %d events=%08X\n",index,rt.epoll_fd,events[i].data.fd,events[i].events);
    175. Session * session = &rt.sessions[index];
    176. if(events[i].data.fd == rt.server_fd)
    177. {
    178. printf("index = %d epollfd = %d cfd = %d\n",index,rt.epoll_fd,events[i].data.fd);
    179. session->accept_cb(rt.epoll_fd,events[i].data.fd,&rt);
    180. }
    181. else
    182. {
    183. if(events[i].events & EPOLLIN)
    184. {
    185. session->read_cb(session);
    186. }
    187. if(events[i].events & EPOLLOUT)
    188. {
    189. session->write_cb(session);
    190. }
    191. }
    192. }
    193. }
    194. }
    195. int reactor_deinit(Reactor &rt)
    196. {
    197. if(rt.epoll_fd >0)
    198. {
    199. close(rt.epoll_fd);
    200. }
    201. return 0;
    202. }
    203. int Accept_cb(int epoll_fd,int fd,void *user_data)
    204. {
    205. if(fd > 0 && epoll_fd >0)
    206. {
    207. int cfd = accept(fd,NULL,NULL);
    208. if(cfd == -1)
    209. {
    210. perror("accept failed");
    211. return -1;
    212. }
    213. set_nonblock(cfd);
    214. printf("Accept_cb epollfd = %d cfd = %d\n",epoll_fd,cfd);
    215. struct epoll_event ev = {0};
    216. ev.data.fd = cfd;
    217. ev.events = EPOLLIN|EPOLLOUT|EPOLLET;
    218. int ret = epoll_ctl(epoll_fd,EPOLL_CTL_ADD,cfd,&ev);
    219. if(ret == -1)
    220. {
    221. perror("epoll_ctrl failed");
    222. return -1;
    223. }
    224. Reactor *reactor = (Reactor*)user_data;
    225. reactor->sessions[cfd].fd = cfd;
    226. //session->fd = cfd;
    227. return 0;
    228. }
    229. return -1;
    230. }
    231. void http_request(Session *session)
    232. {
    233. char method[12]={0},path[512]={0},protocol[20]={0},headers[512]={0};
    234. printf("buf len[%d] content[%s]\n",session->read_offset,session->read_buffer);
    235. char *p = strstr(session->read_buffer,"\r\n\r\n");
    236. int ret = sscanf(session->read_buffer,"%[^ ] %[^ ] %[^ \r\n]%[^\r\n]",method,path,protocol,headers);
    237. printf("sscanf ret is %d headers is %s\n",ret,headers);
    238. if(ret !=3)
    239. {
    240. printf("Wait a whole http header\n");
    241. session->writeable = 0;
    242. return ;
    243. }
    244. else
    245. {
    246. printf("This is a whole http packet\n");
    247. }
    248. session->writeable = 1;
    249. session->read_offset = 0;
    250. if(strcasecmp(method,"get") == 0)
    251. {
    252. if(strcmp(path,"/") == 0)
    253. {
    254. strcpy(session->file_path ,"./");
    255. }
    256. else
    257. {
    258. strcpy(session->file_path ,path+1);
    259. }
    260. struct stat st;
    261. int ret = stat(session->file_path,&st);
    262. if(ret == -1)
    263. {
    264. printf("file doest not exist\n");
    265. //SendHead(event,404,"Not found",GetFileType(".html"),-1);
    266. //SendFile(event,"404.html");
    267. session->is_dir = -1;
    268. return ;
    269. }
    270. if(S_ISDIR(st.st_mode))
    271. {
    272. printf("Directory\n");
    273. //SendHead(event,200,"OK",GetFileType(".html"),-1);
    274. //SendDir(event,file);
    275. session->is_dir = 1;
    276. }
    277. else
    278. {
    279. printf("File\n");
    280. session->file_size = st.st_size;
    281. //SendHead(event,200,"OK",GetFileType(file),st.st_size);
    282. //SendFile(event,file);
    283. session->is_dir = 0;
    284. }
    285. }
    286. }
    287. #define BURSIZE 1024
    288. int hex2dec(char c)
    289. {
    290. if ('0' <= c && c <= '9') {
    291. return c - '0';
    292. } else if ('a' <= c && c <= 'f') {
    293. return c - 'a' + 10;
    294. } else if ('A' <= c && c <= 'F') {
    295. return c - 'A' + 10;
    296. } else {
    297. return -1;
    298. }
    299. }
    300. char dec2hex(short int c)
    301. {
    302. if (0 <= c && c <= 9) {
    303. return c + '0';
    304. } else if (10 <= c && c <= 15) {
    305. return c + 'A' - 10;
    306. } else {
    307. return -1;
    308. }
    309. }
    310. /*
    311. * 编码一个url
    312. */
    313. void urlencode(char url[])
    314. {
    315. int i = 0;
    316. int len = strlen(url);
    317. int res_len = 0;
    318. char res[BURSIZE];
    319. for (i = 0; i < len; ++i) {
    320. char c = url[i];
    321. if (('0' <= c && c <= '9') ||
    322. ('a' <= c && c <= 'z') ||
    323. ('A' <= c && c <= 'Z') || c == '/' || c == '.') {
    324. res[res_len++] = c;
    325. } else {
    326. int j = (short int)c;
    327. if (j < 0)
    328. j += 256;
    329. int i1, i0;
    330. i1 = j / 16;
    331. i0 = j - i1 * 16;
    332. res[res_len++] = '%';
    333. res[res_len++] = dec2hex(i1);
    334. res[res_len++] = dec2hex(i0);
    335. }
    336. }
    337. res[res_len] = '\0';
    338. strcpy(url, res);
    339. }
    340. /*
    341. * 解码url
    342. */
    343. void urldecode(char url[])
    344. {
    345. int i = 0;
    346. int len = strlen(url);
    347. int res_len = 0;
    348. char res[BURSIZE];
    349. for (i = 0; i < len; ++i) {
    350. char c = url[i];
    351. if (c != '%') {
    352. res[res_len++] = c;
    353. } else {
    354. char c1 = url[++i];
    355. char c0 = url[++i];
    356. int num = 0;
    357. num = hex2dec(c1) * 16 + hex2dec(c0);
    358. res[res_len++] = num;
    359. }
    360. }
    361. res[res_len] = '\0';
    362. strcpy(url, res);
    363. }
    364. const char *GetFileType(const char *filename)
    365. {
    366. const char *dot = strrchr(filename,'.');
    367. if(dot == NULL)
    368. {
    369. return "text/plain; charset=utf-8";
    370. }
    371. if(strcmp(dot,".jpg") == 0 ||strcmp(dot,".jpeg") == 0)
    372. {
    373. return "image/jpg";
    374. }
    375. if(strcmp(dot,".html") == 0 ||strcmp(dot,".htm") == 0)
    376. {
    377. return "text/html; charset=utf-8";
    378. }
    379. if(strcmp(dot,".png") == 0)
    380. {
    381. return "image/png";
    382. }
    383. if(strcmp(dot,".bmp") == 0)
    384. {
    385. return "image/bmp";
    386. }
    387. if(strcmp(dot,".gif") == 0)
    388. {
    389. return "image/gif";
    390. }
    391. if(strcmp(dot,".css") == 0)
    392. {
    393. return "text/css";
    394. }
    395. if(strcmp(dot,".mp3") == 0)
    396. {
    397. return "audio/mpeg";
    398. }
    399. return "text/plain; charset=utf-8";
    400. }
    401. int SendHead(int cfd,int status ,const char *desc,const char *type,int size)
    402. {
    403. char buf[4096] = {0};
    404. sprintf(buf,"http/1.1 %d %s\r\n",status,desc);
    405. sprintf(buf+strlen(buf),"content-type: %s\r\n",type);
    406. sprintf(buf+strlen(buf),"content-length: %d\r\n\r\n",size);
    407. printf("SendHead buf[%s]\n",buf);
    408. return send(cfd,buf,strlen(buf),0);
    409. }
    410. int SendDir(Session *session,const char *dirname)
    411. {
    412. char buf[4096] = {0};
    413. sprintf(buf,"%s",dirname);
    414. printf("SendDir dirname=[%s]\n",dirname);
    415. struct dirent **namelist;
    416. int count = scandir(dirname,&namelist,NULL,alphasort);
    417. printf("SendDir count=[%d]\n",count);
    418. for(int i = 0;i< count;i++)
    419. {
    420. char *name = namelist[i]->d_name;
    421. struct stat st;
    422. char sub_path[1024]={0};
    423. sprintf(sub_path,"%s/%s",dirname,name);
    424. stat(sub_path,&st);
    425. if(S_ISDIR(st.st_mode))
    426. {
    427. sprintf(buf+strlen(buf),
    428. "
    429. ",name,name,st.st_size);
    430. }
    431. else
    432. {
    433. sprintf(buf+strlen(buf),
    434. "
    435. ",name,name,st.st_size);
    436. }
    437. //printf("cfd:%d Sendbuf[%s]\n",cfd,buf);
    438. send(session->fd,buf,strlen(buf),0);
    439. memset(buf,0,sizeof(buf));
    440. free(namelist[i]);
    441. }
    442. sprintf(buf,"
    443. %s%ld
      %s%ld
      "
      );
    444. //printf("cfd:%d Sendbuf[%s]\n",cfd,buf);
    445. send(session->fd,buf,strlen(buf),0);
    446. free(namelist);
    447. return 0;
    448. }
    449. int SendFile(Session *session,const char* filename)
    450. {
    451. if(session->file_fd == -1)
    452. {
    453. session->file_fd = open(filename,O_RDONLY);
    454. }
    455. if(session->file_fd >0)
    456. {
    457. #if 1
    458. while(1)
    459. {
    460. char buf[1024];
    461. int len = read(session->file_fd,buf,sizeof (buf));
    462. if(len >0)
    463. {
    464. session->send_file_read_len+=len;
    465. int ret = send(session->fd,buf,len,0);
    466. if(ret >0)
    467. {
    468. session->write_offset += ret;
    469. //printf("This time send [%d] total send [%d] bytes\n",ret,session->write_offset);
    470. }
    471. else if(ret ==0)
    472. {
    473. printf("Send file return 0 close socket this time len = %d total len = %d \n",len,session->send_file_read_len);
    474. close(session->file_fd);
    475. close(session->fd);
    476. }
    477. else
    478. {
    479. int seek_ret = lseek(session->file_fd,session->write_offset,SEEK_SET);
    480. //printf("Seekret = %d session->writeoffset = %d\n",seek_ret,session->write_offset);
    481. if(seek_ret == -1)
    482. {
    483. perror("lseek failed");
    484. }
    485. session->send_file_read_len-=len;
    486. //printf("Send file return -1 wait next send this time len = %d total len = %d\n",len,session->send_file_read_len);
    487. return -1;
    488. }
    489. }
    490. else if(len == 0)
    491. {
    492. printf("Read file end this time len = %d total len = %d\n",len,session->send_file_read_len);
    493. close(session->file_fd);
    494. close(session->fd);
    495. session->write_offset = 0;
    496. session->send_file_read_len = 0;
    497. session->fd = 0;
    498. session->file_fd = -1;
    499. session->writeable = 0;
    500. return 0;
    501. break;
    502. }
    503. else
    504. {
    505. close(session->file_fd);
    506. close(session->fd);
    507. perror("read error");
    508. }
    509. }
    510. #else
    511. off_t offset = 0;
    512. int file_size = lseek(fd,0,SEEK_END);
    513. lseek(fd,0,SEEK_SET);
    514. while(offset
    515. {
    516. int send_len = sendfile(cfd,fd,&offset,file_size-offset);
    517. if(send_len == -1)
    518. {
    519. if(errno == EAGAIN)
    520. {
    521. //perror("sendfile no data send");
    522. }
    523. else
    524. {
    525. perror("sendfile ret -1");
    526. }
    527. }
    528. else
    529. {
    530. printf("Send len:%d\n",send_len);
    531. }
    532. }
    533. #endif
    534. }
    535. else
    536. {
    537. perror("open file failed");
    538. }
    539. //close(fd);
    540. return 0;
    541. }
    542. void http_response(Session *session)
    543. {
    544. //printf("session->writeable = %d\n",session->writeable);
    545. if(session->writeable == 0)
    546. {
    547. printf("Not writable\n");
    548. return ;
    549. }
    550. if(session->is_dir == -1)
    551. {
    552. if(session->head_has_send == 0)
    553. {
    554. SendHead(session->fd,404,"Not found",GetFileType(".html"),-1);
    555. session->head_has_send = 1;
    556. }
    557. SendFile(session,"404.html");
    558. session->writeable = 0;
    559. }
    560. else if(session->is_dir == 1)
    561. {
    562. if(session->head_has_send == 0)
    563. {
    564. SendHead(session->fd,200,"OK",GetFileType(".html"),-1);
    565. session->head_has_send = 1;
    566. }
    567. SendDir(session,session->file_path);
    568. }
    569. else if(session->is_dir == 0)
    570. {
    571. if(session->head_has_send == 0)
    572. {
    573. SendHead(session->fd,200,"OK",GetFileType(session->file_path),session->file_size);
    574. session->head_has_send = 1;
    575. }
    576. SendFile(session,session->file_path);
    577. }
    578. }
    579. int Read_cb(void *user_data)
    580. {
    581. int nread,offset = 0;
    582. if(user_data == NULL) return -1;
    583. Session *sesion = (Session *)(user_data);
    584. printf("Enter readcb1111 sesion->fd = %d\n",sesion->fd);
    585. if(sesion)
    586. {
    587. while ((nread = read(sesion->fd, sesion->read_buffer+sesion->read_offset, 1024-1)) > 0) {
    588. sesion->read_offset += nread;
    589. http_request(sesion);
    590. }
    591. printf("nread = %d\n",nread);
    592. if (nread == -1 && errno != EAGAIN) {
    593. perror("read error");
    594. }
    595. //conn->recv_size = offset;
    596. }
    597. return 0;
    598. }
    599. int Write_cb(void *user_data)
    600. {
    601. if(user_data == NULL) return -1;
    602. Session *session = (Session *)(user_data);
    603. http_response(session);
    604. return 0;
    605. }
    606. int main(int argc ,char *argv[])
    607. {
    608. printf("Reactor\n");
    609. signal(SIGPIPE, SIG_IGN);
    610. Reactor reactor;
    611. reactor_init(reactor,Accept_cb,Read_cb,Write_cb);
    612. reactor_run(reactor);
    613. reactor_deinit(reactor);
    614. return 0;
    615. }

  • 相关阅读:
    网络安全的学习方向和路线是怎么样的?
    基于SSM的大学生创新创业平台竞赛管理子系统设计与实现
    latex图片在双栏文档中横跨两栏
    Qt应用开发(基础篇)——复选按钮 QCheckBox 单选按钮 QRadioButton
    算法框架-LLM-1-Prompt设计(一)
    SpringBoot配置数据库密码加密的方法
    web前端期末大作业网课设计与实现 _简单DIV布局旅游网页——简洁的旅游酒店公寓(15页)html css javascript
    C 风格文件输入/输出---错误处理---(std::clearerr,std::feof,std::ferror,std::perror)
    入门案例:Hello World
    北大肖臻老师《区块链技术与应用》系列课程学习笔记[25]以太坊-智能合约-5
  • 原文地址:https://blog.csdn.net/baoecit/article/details/133589517