C语言实现的一个简单的HTTP程序

2023-05-16

转载自:https://www.cnblogs.com/xuwenmin888/archive/2013/05/04/3059282.html

C语言实现的一个简单的HTTP程序

以下是参考<winsock网络编程经络>中讲解web应用http协议的时候,实现的一个简单的http程序,包含一个服务器和一个客户端。

先贴上客户端的程序:

 

/*************************************************************************

 *

 * Copyright (c) 2012-2013 by xuwm All Rights Reserved

 *

 * FILENAME:  WebClnt.c

 *

 * PURPOSE :  HTTP 客户端程序, 获取网页.

 

 * AUTHOR  :  许文敏

 *

 **************************************************************************/

#include "stdafx.h"

#include <stdio.h>

#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")  /* WinSock使用的库函数 */

/* 定义常量 */

#define HTTP_DEF_PORT     80  /* 连接的缺省端口 */

#define HTTP_BUF_SIZE   1024  /* 缓冲区的大小   */

#define HTTP_HOST_LEN    256  /* 主机名长度 */

char *http_req_hdr_tmpl = "GET %s HTTP/1.1\r\n"

    "Accept: image/gif, image/jpeg, */*\r\nAccept-Language: zh-cn\r\n"

    "Accept-Encoding: gzip, deflate\r\nHost: %s:%d\r\n"

    "User-Agent: Huiyong's Browser <0.1>\r\nConnection: Keep-Alive\r\n\r\n";

/**************************************************************************

 *

 * 函数功能: 解析命令行参数, 分别得到主机名, 端口号和文件名. 命令行格式:

 *           [http://www.baidu.com:8080/index.html]

 *

 * 参数说明: [IN]  buf, 字符串指针数组;

 *           [OUT] host, 保存主机;

 *           [OUT] port, 端口;

 *           [OUT] file_name, 文件名;

 *

 * 返 回 值: void.

 *

 **************************************************************************/

void http_parse_request_url(const char *buf, char *host,

                            unsigned short *port, char *file_name)

{

    int length = 0;

    char port_buf[8];

    char *buf_end = (char *)(buf + strlen(buf));

    char *begin, *host_end, *colon, *file;

    /* 查找主机的开始位置 */

     

    begin = const_cast<char*>(strstr(buf, "//"));

    begin = (begin ? begin + 2 : const_cast<char*>(buf));

     

    colon = strchr(begin, ':');

    host_end = strchr(begin, '/');

    if (host_end == NULL)

    {

        host_end = buf_end;

    }

    else

    {   /* 得到文件名 */

        file = strrchr(host_end, '/');

        if (file && (file + 1) != buf_end)

            strcpy(file_name, file + 1);

    }

    if (colon) /* 得到端口号 */

    {

        colon++;

        length = host_end - colon;

        memcpy(port_buf, colon, length);

        port_buf[length] = 0;

        *port = atoi(port_buf);

        host_end = colon - 1;

    }

    /* 得到主机信息 */

    length = host_end - begin;

    memcpy(host, begin, length);

    host[length] = 0;

}

int main(int argc, char **argv)

{

    WSADATA wsa_data;

    SOCKET  http_sock = 0;         /* socket 句柄 */

    struct sockaddr_in serv_addr;  /* 服务器地址 */

    struct hostent *host_ent;

     

    int result = 0, send_len;

    char data_buf[HTTP_BUF_SIZE];

    char host[HTTP_HOST_LEN] = "127.0.0.1";

    unsigned short port = HTTP_DEF_PORT;

    unsigned long addr;

    char file_name[HTTP_HOST_LEN] = "index.html";

    char file_nameforsave[HTTP_HOST_LEN] = "index1.html";

    FILE *file_web;

    if (argc != 2)

    {

        printf("[Web] input : %s http://www.test.com[:8080]/index.html", argv[0]);

        return -1;

    }

    http_parse_request_url(argv[1], host, &port, file_name);

    WSAStartup(MAKEWORD(2,0), &wsa_data); /* 初始化 WinSock 资源 */

    addr = inet_addr(host);

    if (addr == INADDR_NONE)

    {

        host_ent = gethostbyname(host);

        if (!host_ent)

        {

            printf("[Web] invalid host\n");

            return -1;

        }

         

        memcpy(&addr, host_ent->h_addr_list[0], host_ent->h_length);

    }

    /* 服务器地址 */

    serv_addr.sin_family = AF_INET;

    serv_addr.sin_port = htons(port);

    serv_addr.sin_addr.s_addr = addr;

    http_sock = socket(AF_INET, SOCK_STREAM, 0); /* 创建 socket */

    result = connect(http_sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));

    if (result == SOCKET_ERROR) /* 连接失败 */

    {

        closesocket(http_sock);

        printf("[Web] fail to connect, error = %d\n", WSAGetLastError());

        return -1;

    }

    /* 发送 HTTP 请求 */

    send_len = sprintf(data_buf, http_req_hdr_tmpl, argv[1], host, port);

    result = send(http_sock, data_buf, send_len, 0);

    if (result == SOCKET_ERROR) /* 发送失败 */

    {

        printf("[Web] fail to send, error = %d\n", WSAGetLastError());

        return -1;

    }

    file_web = fopen(file_nameforsave, "a+");

     

    do /* 接收响应并保存到文件中 */

    {

        result = recv(http_sock, data_buf, HTTP_BUF_SIZE, 0);

        if (result > 0)

        {

            fwrite(data_buf, 1, result, file_web);

            /* 在屏幕上输出 */

            data_buf[result] = 0;

            printf("%s", data_buf);

        }

    } while(result > 0);

    fclose(file_web);

    closesocket(http_sock);

    WSACleanup();

    return 0;

}

 首先在vs2010中的,添加一个VC命令行程序,把上面的程序直接放到主程序对应的cpp文件中,然后编译即可。

再贴上服务端的程序:

/*************************************************************************

 *

 * Copyright (c) 2012-2013 by xuwm All Rights Reserved

 *

 * FILENAME:  WebSrv.c

 *

 * PURPOSE :  HTTP 服务器程序, 向客户端提供请求的文件内容.

 

 * AUTHOR  :  许文敏

 *

 **************************************************************************/

#include "stdafx.h"

#include <stdio.h>

#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")  /* WinSock使用的库函数 */

/* 定义常量 */

#define HTTP_DEF_PORT        80     /* 连接的缺省端口 */

#define HTTP_BUF_SIZE      1024     /* 缓冲区的大小 */

#define HTTP_FILENAME_LEN   256     /* 文件名长度 */

/* 定义文件类型对应的 Content-Type */

struct doc_type

{

    char *suffix; /* 文件后缀 */

    char *type;   /* Content-Type */

};

struct doc_type file_type[] =

{

    {"html",    "text/html"  },

    {"gif",     "image/gif"  },

    {"jpeg",    "image/jpeg" },

    { NULL,      NULL        }

};

char *http_res_hdr_tmpl = "HTTP/1.1 200 OK\r\nServer: Huiyong's Server <0.1>\r\n"

    "Accept-Ranges: bytes\r\nContent-Length: %d\r\nConnection: close\r\n"

    "Content-Type: %s\r\n\r\n";

/**************************************************************************

 *

 * 函数功能: 根据文件后缀查找对应的 Content-Type.

 *

 * 参数说明: [IN] suffix, 文件名后缀;

 *

 * 返 回 值: 成功返回文件对应的 Content-Type, 失败返回 NULL.

 *

 **************************************************************************/

char *http_get_type_by_suffix(const char *suffix)

{

    struct doc_type *type;

    for (type = file_type; type->suffix; type++)

    {

        if (strcmp(type->suffix, suffix) == 0)

            return type->type;

    }

    return NULL;

}

/**************************************************************************

 *

 * 函数功能: 解析请求行, 得到文件名及其后缀. 请求行格式:

 *           [GET http://www.baidu.com:8080/index.html HTTP/1.1]

 *

 * 参数说明: [IN]  buf, 字符串指针数组;

 *           [IN]  buflen, buf 的长度;

 *           [OUT] file_name, 文件名;

 *           [OUT] suffix, 文件名后缀;

 *

 * 返 回 值: void.

 *

 **************************************************************************/

void http_parse_request_cmd(char *buf, int buflen, char *file_name, char *suffix)

{

    int length = 0;

    char *begin, *end, *bias;

    /* 查找 URL 的开始位置 */

    begin = strchr(buf, ' ');

    begin += 1;

         

    /* 查找 URL 的结束位置 */

    end = strchr(begin, ' ');

    *end = 0;

    bias = strrchr(begin, '/');

    length = end - bias;

    /* 找到文件名的开始位置 */

    if ((*bias == '/') || (*bias == '\\'))

    {

        bias++;

        length--;

    }

    /* 得到文件名 */

    if (length > 0)

    {

        memcpy(file_name, bias, length);

        file_name[length] = 0;

        begin = strchr(file_name, '.');

        if (begin)

            strcpy(suffix, begin + 1);

    }

}

/**************************************************************************

 *

 * 函数功能: 向客户端发送 HTTP 响应.

 *

 * 参数说明: [IN]  buf, 字符串指针数组;

 *           [IN]  buf_len, buf 的长度;

 *

 * 返 回 值: 成功返回非0, 失败返回0.

 *

 **************************************************************************/

int http_send_response(SOCKET soc, char *buf, int buf_len)

{

    int read_len, file_len, hdr_len, send_len;

    char *type;

    char read_buf[HTTP_BUF_SIZE];

    char http_header[HTTP_BUF_SIZE];

    char file_name[HTTP_FILENAME_LEN] = "index.html", suffix[16] = "html";

    FILE *res_file;

    /* 得到文件名和后缀 */

    http_parse_request_cmd(buf, buf_len, file_name, suffix);

    res_file = fopen(file_name, "rb+"); /* 用二进制格式打开文件 */

    if (res_file == NULL)

    {

        printf("[Web] The file [%s] is not existed\n", file_name);

        return 0;

    }

    fseek(res_file, 0, SEEK_END);

    file_len = ftell(res_file);

    fseek(res_file, 0, SEEK_SET);

     

    type = http_get_type_by_suffix(suffix); /* 文件对应的 Content-Type */

    if (type == NULL)

    {

        printf("[Web] There is not the related content type\n");

        return 0;

    }

    /* 构造 HTTP 首部,并发送 */

    hdr_len = sprintf(http_header, http_res_hdr_tmpl, file_len, type);

    send_len = send(soc, http_header, hdr_len, 0);

    //send_len=1;

    if (send_len == SOCKET_ERROR)

    {

        fclose(res_file);

        printf("[Web] Fail to send, error = %d\n", WSAGetLastError());

        return 0;

    }

    do /* 发送文件, HTTP 的消息体 */

    {

        read_len = fread(read_buf, sizeof(char), HTTP_BUF_SIZE, res_file);

        if (read_len > 0)

        {

            send_len = send(soc, read_buf, read_len, 0);

            file_len -= read_len;

        }

    } while ((read_len > 0) && (file_len > 0));

    fclose(res_file);

     

    return 1;

}

int main(int argc, char **argv)

{

    WSADATA wsa_data;

    SOCKET  srv_soc = 0, acpt_soc;  /* socket 句柄 */

    struct sockaddr_in serv_addr;   /* 服务器地址  */

    struct sockaddr_in from_addr;   /* 客户端地址  */

    char recv_buf[HTTP_BUF_SIZE];

    unsigned short port = HTTP_DEF_PORT;

    int from_len = sizeof(from_addr);

    int result = 0, recv_len;

    if (argc == 2) /* 端口号 */

        port = atoi(argv[1]);

    WSAStartup(MAKEWORD(2,0), &wsa_data); /* 初始化 WinSock 资源 */

     

    srv_soc = socket(AF_INET, SOCK_STREAM, 0); /* 创建 socket */

    if (srv_soc == INVALID_SOCKET)

    {

        printf("[Web] socket() Fails, error = %d\n", WSAGetLastError());

        return -1;

    }

     

    /* 服务器地址 */

    serv_addr.sin_family = AF_INET;

    serv_addr.sin_port = htons(port);

    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    result = bind(srv_soc, (struct sockaddr *) &serv_addr, sizeof(serv_addr));

    if (result == SOCKET_ERROR) /* 绑定失败 */

    {

        closesocket(srv_soc);

        printf("[Web] Fail to bind, error = %d\n", WSAGetLastError());

        return -1;

    }

    result = listen(srv_soc, SOMAXCONN);

    printf("[Web] The server is running ... ...\n");

    while (1)

    {

        acpt_soc = accept(srv_soc, (struct sockaddr *) &from_addr, &from_len);

        if (acpt_soc == INVALID_SOCKET) /* 接受失败 */

        {

            printf("[Web] Fail to accept, error = %d\n", WSAGetLastError());

            break;

        }

        printf("[Web] Accepted address:[%s], port:[%d]\n",

            inet_ntoa(from_addr.sin_addr), ntohs(from_addr.sin_port));

        recv_len = recv(acpt_soc, recv_buf, HTTP_BUF_SIZE, 0);

        if (recv_len == SOCKET_ERROR) /* 接收失败 */

        {

            closesocket(acpt_soc);

            printf("[Web] Fail to recv, error = %d\n", WSAGetLastError());

            break;

        }

        recv_buf[recv_len] = 0;

        /* 向客户端发送响应数据 */

        result = http_send_response(acpt_soc, recv_buf, recv_len);

        closesocket(acpt_soc);

    }

     

    closesocket(srv_soc);

    WSACleanup();

    printf("[Web] The server is stopped.\n");

    return 0;

}

 这个也跟客户端程序一样,打开VS2010,新建一个VC命令行程序,COPY上面的代码,直接放到主程序的CPP文件中,编译即可。

运行代码如下:

1.先运行服务端程序,绑定端口,然后开启监听  在CMD里先切换到exe的目录,然后 输入 服务端程序名.exe 9000,此处服务端程序名换成对应的程序名称.后面的9000端口号,也可以换成别的。

 2. 再运行客户羰程序,同上面一样,切换到exe 的目录,然后输入 客户端程序名.exe http://127.0.0.1:9000/index.html,  此处客户端程序名换成对应的程序名称,后面的http://127.0.0.1:9000/index.html,代表请求的网页路径。

 3. 在服务器的exe目录下,应创建一个index.html文件,里面可以输入一个正规的html文件。

以上只是学习网络编程的一点小体会,尽当以后温故:)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言实现的一个简单的HTTP程序 的相关文章

随机推荐

  • 阿里内推面试经历

    写在最前 其实主观上并不是很想写 xff0c 但坚持写完是希望能分享给准备进互联网实习或工作的同学或朋友一些经验和收获
  • 关于利用Openmp中使用的时间函数

    Openmp是一项并行化技术 xff0c 是可以提高串行化程序的运行效率的 xff0c 但需要使用正确的时间函数来进行衡量 首先 xff0c 先提出一个unix linux下的内核时间获取函数和一个omp h的一个库函数 1 clock 函
  • mybatis之映射文件

    mybatis框架如何实现java语句与数据库语句的分离 映射文件 通过在映射文件中写入动态sql语句 xff0c 完成增删改查操作 映射文件中的元素都包含在根节点 lt mapper gt lt mapper gt 下 xff0c map
  • mybatis之高级查询

    Mybatis中的高级查询主要通过关联查询 xff0c 集合查询或鉴别器来完成 其核心就是之前提到的通过resultMap标记来完成 1 关联查询 关联查询一般有三种方式 xff1a a 联合查询 利用resultMap的map xml中的
  • mybaits之动态sql

    mybaits除了提供连接数据库 xff0c 使java和数据库语句分离之外 xff0c 还有一个显著的特点就是使用动态sql语句 这些sql语句均写在map映射文件中 xff0c 并通过一系列标记来完成 1 if标记 常用形式 xff1a
  • 平衡二叉树-的四种旋转调整(代码,图解)

    平衡二叉树 的四种旋转调整 xff08 代码 xff0c 图解 xff09 1 右单旋 xff1a 新插入节点插入在较高左子树的左侧 xff08 左左右 xff09 xff0c 插入新节点二十 xff1a 1 修改parent和curLR的
  • 头文件里一般会加入的宏定义,为了避免一个头文件被重复调用

    华清视频里讲的 xff0c 写代码的时候 xff0c 头文件一般地都会加上一个类似这样的宏 xff0c 希望你从此以后再写头文件 xff0c 加上一个宏 加了这个有一个好处 xff0c 当你第一次包含tree h的时候 xff0c 如果没定
  • 自己组装Pixhawk F450无人机的一些细节

    首先参考文档为 xff1a 1 https mp weixin qq com s VXKU kIB v i0AX3zgtLig 2 https mp weixin qq com s Qzzl dQ6Tz2pXNp7Oj0lTg 3 http
  • 电机和桨叶要搭配选择

    拍自 四旋翼无人机的制作与飞行
  • 接收机PPM与SBUS

    最开始是自己弄ACfly的飞控时发现插接收机有两个位置 xff0c 一个PPM一个SBUS xff0c 我想直接移植Pixhawk的接收机或者无名的接收机到ACfly模块上 我最后发现Pixhawk和无名的也是留了两个给接收机 xff08
  • 现在发现如果无人机的电机不同,浆可能是不能混用的。

    现在发现如果无人机的电机不同 xff0c 浆可能是不能混用的 孔位不同 xff0c 我之前在无名那里买了很多浆 xff0c 觉得这次F330的浆没了可以用那个替 xff0c 我刚刚试了下发现插不进去 xff0c 不能通用 包括我刚才在店家那
  • 无人机电池似乎可以并联,串联组合

    之前总是见到这种奇怪的线 xff0c 一直不知道作什么用 xff0c 现在大概清楚了 是不是这样可以实现更长时间的续航 xff0c 我之前再ACfly的群里看到一个人的六轴是上面放了两个电池的 xff0c 他这可能也是并联的
  • 任务的三要素是任务主体函数,任务栈和任务控制块

    任务的三要素是任务主体函数 xff0c 任务栈和任务控制块 由xTaskCreateStatic 函数来把三者联合起立 下面拍自野火的 FreeRTOS内核实现与应用开发实战指南
  • 如何用Realsense D435i运行VINS-Mono等VIO算法 获取IMU同步数据

    摘自 xff1a https blog csdn net qq 41839222 article details 86552367 如何用Realsense D435i运行VINS Mono等VIO算法 获取IMU同步数据 Manii 20
  • Opencv安装与环境配置

    转载自 xff1a https blog csdn net sm16111 article details 81238324 Opencv安装与环境配置 代码敌敌畏 2018 07 27 15 46 24 50411 收藏 94 分类专栏
  • 串口参数详解:波特率,数据位,停止位,奇偶校验位

    转载自 xff1a https blog csdn net sinat 35705952 article details 89034455 串口参数详解 xff1a 波特率 xff0c 数据位 xff0c 停止位 xff0c 奇偶校验位 W
  • cpp-httplib库简单原理,听说你还不会开源库?

    cpp httplib库的原理 听说你还不会开源库 xff1f 介绍httplib h头文件的处理流程httplib h头文件的组成httplib h头文件搭建服务端与客户端的原理Get接口listen 0 0 0 0 8989 接口 介绍
  • UART串口调试

    转载自 xff1a https www secpulse com archives 157847 html UART串口调试 脉搏文库 TideSec 2021 04 23 4 356 0x00前言 前段时间陆陆续续的对光猫 路由器 摄像头
  • visca协议及其实现的简单认识

    转载自 xff1a https latelee blog csdn net article details 35811777 visca协议及其实现的简单认识 李迟 2014 06 30 14 09 01 7064 收藏 12 分类专栏 x
  • C语言实现的一个简单的HTTP程序

    转载自 xff1a https www cnblogs com xuwenmin888 archive 2013 05 04 3059282 html C语言实现的一个简单的HTTP程序 以下是参考 lt winsock网络编程经络 gt