• 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文件。

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

  • 相关阅读:
    数据库索引的作用和优势缺点
    Python 新浪微博元素 (Word, Screen Name)词汇多样性
    解决Myeclipse在port占用,导致tomcat无法启动。(Linux)
    linux命名管道通信过程
    Lua环境搭建之使用EditPlus搭建Lua开发环境
    详解LUA开发工具及其环境配置
    UltraEdit配置python和lua环境
    Lua学习笔记
    Linux 安装ibus极点五笔输入法备忘录
    win2k/xp查看当前进程
  • 原文地址:https://www.cnblogs.com/xuwenmin888/p/3059282.html
Copyright © 2020-2023  润新知