• HTTP(0)-并发服务器


    1.客户端请求+服务器响应协议说明及流程
    2.服务器接收http请求
    3.服务器解析http请求
    4.服务器响应http请求
    5.并发处理


    1.客户端请求+服务器响应

    流程如下:


    本质上就是读取字节流,解析字节流的信息,得到文件名,在服务器上找到文件,然后再按格式发出去。

    注意:字段名: 后面是有空格的。请求头部结束是有回车换行的。

    1.主函数

    1. #include //sockaddr_in
    2. #include
    3. #include
    4. #include
    5. #include
    6. #define SERVER_PORT 80
    7. typedef int socklen_t;
    8. static int debug = 0;
    9. int get_line(int sock,char *buf,int size);
    10. void* do_http_request(void* arg);
    11. void do_http_response(int client_sock,const char *path);
    12. void headers(int client_sock, FILE * resource);
    13. void cat(int client_sock, FILE* resource);
    14. //异常处理
    15. void bad_request(int client_sock);//400
    16. void unimplemented(int client_sock);//501
    17. void not_found(int client_sock);
    18. int httpmain(void)
    19. {
    20. //1.创建连接
    21. int sock;
    22. struct sockaddr_in server_addr;
    23. sock = socket(AF_INET,SOCK_STREAM,0);
    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. int done = 1;
    31. //2.解析http
    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. sock = socket(AF_INET, SOCK_STREAM, 0);
    40. //bzero(&server_addr, sizeof(server_addr));//清空标签
    41. server_addr.sin_family = AF_INET;
    42. server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    43. server_addr.sin_port = htons(SERVER_PORT);
    44. //打印客户端IP地址和端口号
    45. /*printf("client ip:%s\t port:%d\n",
    46. inet_ntop(AF_INET,&client.sin_addr,client_ip,sizeof(client_ip)),
    47. ntohs(client.sin_port));
    48. //读取客户端发送的数据
    49. len = read(client_sock, buf, sizeof(buf) - 1);
    50. buf[len] = '\0';
    51. //转换成大写
    52. for (i = 0; i < len; i++)
    53. {
    54. buf[i] = toupper(buf[i]);
    55. }
    56. len = write(client_sock, buf, len);
    57. printf("finished len:%d\n", len);
    58. close(client_sock);*/
    59. int* pclient_sock = NULL;
    60. //启动线程处理http请求
    61. pclient_sock = (int*)malloc(sizeof(int));
    62. *pclient_sock = client_sock;
    63. pthread_create(&id, NULL, do_http_request, pclient_sock);
    64. close(client_sock);
    65. }
    66. close(sock);
    67. return 0;
    68. }

    3.服务器接收http请求
      3.1读取每一行  int get_line(int sock, char* buf, int size)
     

    1. //读取每一行:返回值:-1:读取出错;0:读空行;>0:成功读取
    2. int get_line(int sock, char* buf, int size) {
    3. int count = 0;
    4. char ch = '\0';
    5. int len = 0;
    6. while ((count-1)&&ch!='\n')
    7. {
    8. len = read(sock, &ch, 1);
    9. if (len == 1)//如果读取正常
    10. {
    11. if (ch == '\r')//如果是回车
    12. {
    13. continue;
    14. }
    15. else if (ch == '\n')//如果是换行
    16. {
    17. break;
    18. }
    19. buf[count] = ch;
    20. count++;
    21. }
    22. else if(len==-1)//如果读取异常
    23. {
    24. count = -1;
    25. break;
    26. }
    27. }
    28. if (count >= 0)buf[count] = '\0';
    29. return count;
    30. }

    3.2//解析请求:GET url 文件  void* do_http_request(void* arg);
    如果碰到2个连续的回车换行即意味着请求头部结束了。

    1. void* do_http_request(void* arg) {
    2. int len = 0;
    3. char buf[256];
    4. char method[64];
    5. char url[256];
    6. char path[256];
    7. struct stat st;
    8. int client_sock = *(int*)arg; //为了被线程调用,必须要改成指针
    9. len = get_line(client_sock, buf, sizeof(buf)); //1.读取请求行
    10. if (len > 0)
    11. {
    12. int i = 0, j = 0;
    13. while (!isspace(buf[j]) && (i < sizeof(method) - 1))
    14. {
    15. method[i] = buf[j];
    16. i++;
    17. j++;
    18. }
    19. method[i] = '\0';
    20. if (debug) printf("request method:%s\n", method);
    21. if (strncasecmp(method,"GET",i)==0) //2.GET方法 strncasecmp:比较参数s1和s2字符串前n个字符
    22. {
    23. if (debug) printf("method=GET\n");
    24. while (isspace(buf[j++])); // isspace跳过白空格
    25. i = 0;
    26. while (!isspace(buf[j]) && (i < sizeof(url) - 1)) //3.得到URL
    27. {
    28. url[i] = buf[j];
    29. i++;
    30. j++;
    31. }
    32. url[i]= '\0';
    33. if (debug) printf("url:%s\n", url);
    34. char* pos = strchr(url, "?"); //4.获取文件 4.1截取 ?前的路径
    35. if (pos)
    36. {
    37. *pos = '\0';
    38. printf("real url: %s\n",url); //index.html ->./html_docs/index.html
    39. }
    40. sprintf(path, "./html_doc%s", url); //定位 只能截取一个/ path就是主目录下的全文件路径
    41. if (path[strlen(path)-1]=='/') //如果只有/那么就显示index.html
    42. {
    43. strcat(path, "index.html");
    44. }
    45. if (debug) printf("path:%s\n", path);
    46. do { //5.不断的读
    47. len = get_line(client_sock, buf, sizeof(buf));
    48. } while (len > 0);
    49. if (stat(path, $st) == -1) //6.判断文件是否存在
    50. {
    51. not_found(client_sock);
    52. }
    53. else
    54. {
    55. if (S_ISDOR(st.st_mode)) // 文件存在 S_ISDOR
    56. {
    57. strcat(path, "/index.html");
    58. }
    59. do_http_response(client_sock, path); //7.响应
    60. }
    61. }
    62. else {
    63. bad_request(client_sock); //8.出错处理
    64. }
    65. if (arg) free(arg);
    66. close(client_sock);
    67. return NULL;
    68. }
    69. }

     

       

    4.服务器响应http请求

      

    1. void do_http_response(int client_sock,const char *path);
    2. void headers(int client_sock, FILE * resource);
    3. void cat(int client_sock, FILE* resource);
    4. //响应:把文件发出去
    5. void do_http_response(int client_sock, const char* path){
    6. FILE* resource = NULL;
    7. resource = fopen(path, "r");
    8. if (resource == NULL) {
    9. not_found(client_sock);
    10. }
    11. else
    12. {
    13. headers(client_sock, resource);
    14. cat(client_sock, resource);
    15. }
    16. fclose(resource);
    17. sleep(20);
    18. }
    19. void headers(int client_sock, FILE* resource){
    20. char buf[1024];
    21. char tmp[64];
    22. int fileid = 0;
    23. struct stat st;
    24. strcpy(buf,"HTTP/1.0 200 OK\r\n");
    25. strcpy(buf, "Server: Martin Server\r\n");
    26. strcpy(buf, "Content-Type text/html\r\n");
    27. strcpy(buf, "Connection:Close\r\n");
    28. fileid = fileno(resource);
    29. if (fstat(fileid, &st) == -1) {
    30. inner_error(client_sock);
    31. }
    32. snprintf(tmp,64,"Content-Length: %ld\r\n\r\n",st.st_size);
    33. strcat(buf, tmp);
    34. if (send(client_sock, buf, strlen(buf), 0) < 0)
    35. {
    36. fprintf(stderr,"send failed %s, %s\n",buf,strerror(errno));
    37. }
    38. }
    39. void cat(int client_sock, FILE* resource){
    40. char buf[1024];
    41. fgets(buf, sizeof(buf), resource);
    42. while (!feof(resource))
    43. {
    44. int len = write(client_sock, buf, strlen(buf));
    45. if (len > 0)printf("%s", buf);
    46. fgets(buf, sizeof(buf), resource);
    47. }
    48. }

    发的内容就是字符串  header+body.

      截取?之前的字符,得到文件名。

       
    判断文件是否存在于服务器上 ,用struct stat st;

     如果不存在 



     
    内部错误

      

     5.并发
    就是用线程处理http的请求以下为注意点:

    1.添加线程指针       int *pclient_sock=NULL;
    2.函数返回值改成指针   void * do_http_request(void * pclient_sock)
    3.函数传递的参数改成指针
    void * do_http_request(void * pclient_sock)
    {
    int client_sock=*(int *)pclient_sock;
    close(client_sock);
    if(pclient_sock) free(pclient_sock);//释放动态分配的内存
    return NULL;

    }

     

     

     

  • 相关阅读:
    微软 CEO 纳德拉痛失爱子
    C++ 定义一个地图类和地点类,开发一个小游戏。
    [SWPUCTF 2022 新生赛]善哉善哉(隐写,新佛曰,MD5)
    IOS开发之页面跳转
    React 函数组件
    感叹之余随手记—他山之石,可以攻玉
    SpringCloud解决feign调用token丢失问题
    Long类型的数据,后端传给前端产生的精度丢失问题
    column 属性实现图片瀑布流
    【更新】囚生CYのMemo(20231118~)
  • 原文地址:https://blog.csdn.net/aggie4628/article/details/126171492