• C/C++实现简单高并发http服务器


    基础知识

    html,全称为html markup language,超文本标记语言

    http,全称hyper text transfer protocol,超文本传输协议。用于从万维网(WWW:World Wide Web)服务器传输超文本到本地浏览器的传送协议。

    客户端请求的格式:

     

    请求方法有:GET、POST等。URL:请求地址。协议版本:HTTP的版本。

    服务器响应的格式:

     

    -----响应代号代号描述
    服务器上存在请求的内容,并可以响应给客户端200OK
    客户端的请求有异常,方法有问题501Method Not Implemented
    服务器收到请求后,因为自生的问题没法响应500Internal Server Error
    请求的内容不存在404NOT FOUND
    客户端发送的请求格式有问题等(一般不存在)400BAD REQUEST

    http服务器实现

    文件概念

    文件的Inode元信息表示文件的索引节点,存储着文件的元信息,例如文件得创建者,文件创建日期,文件大小等。每个inode都有一个号码,操作系统用inode号码来识别不同的文件,使用命令ls -i可以查看inode号码。

    stat函数

    stat是C++用于读取文件资源管理器的库函数,头文件为:

    1. #include
    2. #include
    3. #include
    4. int stat(const char *path,struct stat *buf);
    5. int fstat(int fd,struct stat *buf);
    6. int lstat(const char *path,struct stat *buf);
    7. parameter:
    8. path:文件路径
    9. buf:传入的保存文件状态的指针,用于保存文件的状态
    10. fd:文件描述符
    11. return:成功返回0,失败返回-1,并设置errno

    stat的结构体内容如下所示:

    1. struct stat {
    2. dev_t st_dev; /* ID of device containing file */
    3. ino_t st_ino; /* inode number */
    4. mode_t st_mode; /* S_ISREG(st_mode) 是一个普通文件 S_ISDIR(st_mode) 是一个目录*/
    5. nlink_t st_nlink; /* number of hard links */
    6. uid_t st_uid; /* user ID of owner */
    7. gid_t st_gid; /* group ID of owner */
    8. dev_t st_rdev; /* device ID (if special file) */
    9. off_t st_size; /* total size, in bytes */
    10. blksize_t st_blksize; /* blocksize for filesystem I/O */
    11. blkcnt_t st_blocks; /* number of 512B blocks allocated */
    12. time_t st_atime; /* time of last access */
    13. time_t st_mtime; /* time of last modification */
    14. time_t st_ctime; /* time of last status change */
    15. };

     

    并发和并行

    并发与并行的区别简单来说所谓的并发指的是多个进程按照一定的时间间隔进行,只不过这个时间间隔很小,人类难以感受到而已,实际上在微观角度,进程的并发执行还是顺序执行

    高并发:高并发是互联网分布式框架设计中必须要考虑的因素之一,通常指的是,通过设计系统能够同时并行处理很多请求。

    线程可以并行的执行任务

    1. //头文件
    2. #include
    3. //函数
    4. int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg);
    5. pthread_t:当前Linux中可理解为:typedef unsigned long int pthread_t
    6. args1:传出参数,保存系统为我们分配好的线程ID;
    7. args2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
    8. args3:函数指针,指向线程主函数(线程体),函数运行结束,则线程结束。
    9. args4:线程主函数执行期间所需要使用的函数。

    在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。start_routine函数接收一个参数,是通过pthread_createarg参数传递给它的,该参数的类型为void *,这个指针按什么类型解释由调用者自己定义。start_routine的返回值类型也是void *,这个指针的含义同样由调用者自己定义。start_routine返回时,这个线程就退出了,其它线程可以调用pthread_join得到start_routine的返回值。
    pthread_create成功返回后,新创建的线程的id被填写到thread参数所指向的内存单元。
    attr参数表示线程属性。

    pthread_exit (status) 
    

    pthread_exit用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。
    如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

    gcc/g++ 编译时需要添加 -pthread进行编译。

    gcc test.c -pthread -o test

    简单的多线程实例:

    1. #include <iostream>
    2. #include <cstdlib>
    3. #include <pthread.h>
    4. using namespace std;
    5. #define NUM_THREADS 5
    6. void *PrintHello(void *threadid)
    7. {
    8. // 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取
    9. int tid = *((int*)threadid);
    10. cout << "Hello Runoob! 线程 ID, " << tid << endl;
    11. pthread_exit(NULL);
    12. }
    13. int main ()
    14. {
    15. pthread_t threads[NUM_THREADS];
    16. int indexes[NUM_THREADS];// 用数组来保存i的值
    17. int rc;
    18. int i;
    19. for( i=0; i < NUM_THREADS; i++ ){
    20. cout << "main() : 创建线程, " << i << endl;
    21. indexes[i] = i; //先保存i的值
    22. // 传入的时候必须强制转换为void* 类型,即无类型指针
    23. rc = pthread_create(&threads[i], NULL,
    24. PrintHello, (void *)&(indexes[i]));
    25. if (rc){
    26. cout << "Error:无法创建线程," << rc << endl;
    27. exit(-1);
    28. }
    29. }
    30. pthread_exit(NULL);
    31. }

    最终服务器代码:

    1. #include <stdio.h>
    2. #include <unistd.h>
    3. #include <sys/types.h>
    4. #include <sys/socket.h>
    5. #include <sys/stat.h>
    6. #include <ctype.h>
    7. #include <arpa/inet.h>
    8. #include <errno.h>
    9. #include<pthread.h>
    10. #define SERVER_PORT 80
    11. void do_http_request(int client_sock);
    12. int get_line(int client_sock, char *buf, int size);
    13. void do_http_response(int client_sock, const char *path);
    14. void headers(int client_sock, FILE *resource);
    15. void cat(int client_sock, FILE *resource);
    16. void not_found(int client_sock);
    17. void inner_error(int client_sock);
    18. int main(void)
    19. {
    20. int sock;
    21. struct sockaddr_in server_addr;
    22. sock = socket(AF_INET, SOCK_STREAM, 0);
    23. // printf("wait \n");
    24. bzero(&server_addr, sizeof(server_addr));
    25. server_addr.sin_family = AF_INET;
    26. server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    27. server_addr.sin_port = htons(SERVER_PORT);
    28. bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
    29. listen(sock, 128);
    30. printf("wait client connect\n");
    31. int done = 1;
    32. while (done)
    33. {
    34. struct sockaddr_in client;
    35. int client_sock, len, i;
    36. char client_ip[64];
    37. char buf[256];
    38. socklen_t client_addr_len;
    39. client_addr_len = sizeof(client);
    40. client_sock = accept(sock, (struct sockaddr *)&client, &client_addr_len);
    41. printf("client ip: %s \t port is : %d \n", inet_ntop(AF_INET, &client.sin_addr.s_addr, client_ip, sizeof(client_ip)), ntohs(client.sin_port));
    42. pthread_t tid;
    43. int* ptr_int=NULL;
    44. int err=0;
    45. ptr_int=(int*)malloc(sizeof(int));
    46. *ptr_int=client_sock;
    47. if(err=pthread_create(&tid,NULL,do_http_request,(void*)ptr_int)){
    48. printf(stderr,"cannot create thread. reason: %s\n",strerror(errno));
    49. if(ptr_int) free(ptr_int);
    50. }
    51. // do_http_request(client_sock);
    52. // close(client_sock);
    53. }
    54. close(sock);
    55. return 0;
    56. }
    57. void* do_http_request(void* p_client_sock)
    58. {
    59. int len = 0;
    60. char buf[256], method[64], url[256], path[256];
    61. int client_sock=*(int *)p_client_sock;
    62. struct stat st;
    63. // http response struct: request_method url protocol_version \r\n
    64. len = get_line(client_sock, buf, sizeof(buf));
    65. if (len > 0)
    66. {
    67. int i = 0, j = 0;
    68. while (!isspace(buf[j]) && i < sizeof(method) - 1)
    69. {
    70. method[i] = buf[j];
    71. i++;
    72. j++;
    73. }
    74. // set end tag
    75. method[i] = '\0';
    76. printf("method: %s\n", method);
    77. // method is GET request
    78. if (strncasecmp(method, "GET", i) == 0)
    79. {
    80. printf("request method is GET\n");
    81. while (isspace(buf[j]))
    82. {
    83. j++;
    84. }
    85. i = 0;
    86. while (!isspace(buf[j]) && i < sizeof(url) - 1)
    87. {
    88. url[i] = buf[j];
    89. i++;
    90. j++;
    91. }
    92. url[i] = '\0';
    93. if (strncasecmp(url, "/favicon.ico", i) == 0)
    94. {
    95. strcpy(url, "/hello.html");
    96. }
    97. printf("url:%s\n", url);
    98. // read surplus request
    99. do
    100. {
    101. len = get_line(client_sock, buf, sizeof(buf));
    102. printf("%s\n", buf);
    103. } while (len > 0);
    104. // get local url file, and process ? in url, eg. url=128.0.0.2/hel.html?wang=dedefe
    105. char *pos = strchr(url, '?');
    106. if (pos)
    107. {
    108. // \0 represent string end tag
    109. *pos = '\0';
    110. printf("real url: %s\n", url);
    111. }
    112. sprintf(path, "./html_doc%s", url);
    113. printf("path:%s\n", path);
    114. // execute http response
    115. // if file is exist, to response 200, ok,and send html file,else response 404 NOT FOUND
    116. if (stat(path, &st) == -1)
    117. {
    118. printf("--------------------");
    119. fprintf(stderr, "stat %s failed. reason :%s\n", strerror(errno));
    120. not_found(client_sock);
    121. }
    122. else
    123. {
    124. printf("*****************");
    125. if (S_ISDIR(st.st_mode))
    126. {
    127. strcat(path, "/index.html");
    128. }
    129. do_http_response(client_sock, path);
    130. }
    131. }
    132. else
    133. {
    134. // request method is not GET,read http head, and response client request
    135. fprintf(stderr, "warning, other request [%s]\n", method);
    136. do
    137. {
    138. len = get_line(client_sock, buf, sizeof(buf));
    139. printf("%s\n", buf);
    140. } while (len > 0);
    141. // unimplement()
    142. }
    143. }
    144. else
    145. {
    146. printf("method is error");
    147. }
    148. close(client_sock);
    149. if(p_client_sock) free(p_client_sock);
    150. }
    151. void do_http_response(int client_sock, const char *path)
    152. {
    153. FILE *resource = NULL;
    154. resource = fopen(path, "r");
    155. if (resource == NULL)
    156. {
    157. not_found(client_sock);
    158. return;
    159. }
    160. // send http head
    161. headers(client_sock, resource);
    162. // send http body
    163. cat(client_sock, resource);
    164. // printf("end response!!!!");
    165. fclose(resource);
    166. }
    167. void headers(int client_sock, FILE *resource)
    168. {
    169. struct stat st;
    170. int fileid = 0;
    171. char temp[64];
    172. char buf[1024] = {0};
    173. strcpy(buf, "HTTP/1.0 200 OK\r\n");
    174. strcat(buf, "Server: Martin Server\r\n");
    175. strcat(buf, "Content-Type: text/html\r\n");
    176. strcat(buf, "Connection: Close\r\n");
    177. fileid = fileno(resource);
    178. /* fstat: Get file attributes for the file, device, pipe, or socket
    179. that file descriptor FD is open on and put them in BUF. */
    180. if (fstat(fileid, &st) == -1)
    181. {
    182. inner_error(client_sock);
    183. }
    184. snprintf(temp, 64, "Content-Length:%d\r\n\r\n", st.st_size);
    185. strcat(buf, temp);
    186. printf(stdout, "header: %s", buf);
    187. if (send(client_sock, buf, strlen(buf), 0) < 0)
    188. {
    189. fprintf(stderr, "send fail,data %s, reason %s", buf, strerror(errno));
    190. }
    191. }
    192. void cat(int client_sock, FILE *resource)
    193. {
    194. char buf[1024];
    195. fgets(buf, sizeof(buf), resource);
    196. while (!feof(resource))
    197. {
    198. int len = write(client_sock, buf, strlen(buf));
    199. if (len < 0)
    200. {
    201. fprintf(stderr, "send boady error. reason %s\n", strerror(errno));
    202. break;
    203. }
    204. fprintf(stdout, "%s", buf);
    205. fgets(buf, sizeof(buf), resource);
    206. }
    207. }
    208. int get_line(int client_sock, char *buf, int size)
    209. {
    210. int count = 0;
    211. char ch = '\0';
    212. int len = 0;
    213. while (count < size - 1 && ch != '\n')
    214. {
    215. len = read(client_sock, &ch, 1);
    216. if (len == 1)
    217. {
    218. if (ch == '\r')
    219. continue;
    220. else if (ch == '\n')
    221. break;
    222. buf[count] = ch;
    223. count++;
    224. }
    225. else if (len == -1)
    226. {
    227. perror("read fail");
    228. count = -1;
    229. break;
    230. }
    231. else
    232. {
    233. fprintf(stderr, "client close.\n");
    234. count = -1;
    235. break;
    236. }
    237. }
    238. if (count >= 0)
    239. buf[count] = '\0';
    240. return count;
    241. }
    242. void not_found(int client_sock)
    243. {
    244. const char *reply = "HTTP/1.0 404 NOT FOUND\r\n\
    245. Content-Type: text/html\r\n\
    246. \r\n\
    247. zh-CN\">\r\n\
    248. text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\
    249. \r\n\
    250. NOT FOUND\r\n\
    251. \r\n\
    252. \r\n\
    253. 文件不存在!\r\n\

    254. The server could not fulfill your request because the resource specified is unavailable or none xistent.\r\n\

    255. \r\n\
    256. ";
    257. int len = write(client_sock, reply, strlen(reply));
    258. fprintf(stdout, reply);
    259. if (len < 0)
    260. {
    261. fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
    262. }
    263. }
    264. void inner_error(int client_sock)
    265. {
    266. const char *reply = "HTTP/1.0 500 Internal Sever Error\r\n\
    267. Content-Type: text/html\r\n\
    268. \r\n\
    269. zh-CN\">\r\n\
    270. text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\
    271. \r\n\
    272. Inner Error\r\n\
    273. \r\n\
    274. \r\n\
    275. 服务器内部出错.\r\n\

    276. \r\n\
    277. ";
    278. int len = write(client_sock, reply, strlen(reply));
    279. fprintf(stdout, reply);
    280. if (len <= 0)
    281. {
    282. fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
    283. }
    284. }

    最终客户端代码:

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <string.h>
    4. #include <unistd.h>
    5. #include <sys/socket.h>
    6. #include <netinet/in.h>
    7. #define SERVER_PORT 666
    8. #define SERVER_IP "127.0.0.1"
    9. int main(int argc, char *argv[])
    10. {
    11. int sockfd;
    12. char *message;
    13. struct sockaddr_in servaddr;
    14. int n;
    15. char buf[64];
    16. if (argc != 2)
    17. {
    18. fputs("Usage: ./echo_client message \n", stderr);
    19. exit(1);
    20. }
    21. message = argv[1];
    22. printf("message: %s\n", message);
    23. sockfd = socket(AF_INET, SOCK_STREAM, 0);
    24. // 重置结构体的内存空间
    25. memset(&servaddr, '\0', sizeof(struct sockaddr_in));
    26. servaddr.sin_family = AF_INET;
    27. inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr);
    28. servaddr.sin_port = htons(SERVER_PORT);
    29. connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    30. write(sockfd, message, strlen(message));
    31. n = read(sockfd, buf, sizeof(buf) - 1);
    32. if (n > 0)
    33. {
    34. buf[n] = '\0';
    35. printf("receive: %s\n", buf);
    36. }
    37. else
    38. {
    39. perror("error!!!");
    40. }
    41. printf("finished.\n");
    42. close(sockfd);
    43. return 0;
    44. }

  • 相关阅读:
    VScode简单配置深度学习环境(连接远程服务器)
    new bing 初体验:辅助看论文刚刚好
    Windows11安装Maven,配置环境变量
    redis数据库基础知识
    零拷贝并非万能解决方案:重新定义数据传输的效率极限
    [LabVIEW]圖像內的物件計算_Count objects
    高德地图api接口免费查询天气实战案例,axios请求查询天气,js版,【接上一篇微信测试号推送纪念日】
    数据结构-红黑树
    【misc】buu-面具下的flag——zip伪加密+用NTFS流隐藏文件
    批处理编写
  • 原文地址:https://blog.csdn.net/hulinhulin/article/details/133755723