以下是参考<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文件。

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

C语言实现的一个简单的HTTP程序相关推荐

  1. 编程题目:使用C++语言模拟完成一个简单的计算机系统

    编程题目:使用C++语言模拟完成一个简单的计算机系统(举一个管理人员的例子,其他的例子与下述方法一致即可) #include<iostream.h> #include<math.h& ...

  2. c语言设计程序计算器,C语言程序设计,做一个简单计算器

    题目: C语言程序设计,做一个简单计算器 程序内容有:加减乘除和平方,立方,开方等.用if或者swith结构来编程. 算加法时只输出加法的结果,算减法时只出减法的结果,如此类推. 解答: 已发送, # ...

  3. python 消息队列 go_gmq: gmq是基于redis提供的特性,使用go语言开发的一个简单易用的消息队列;支持延迟任务,异步任务,超时任务,优先级任务...

    1. 概述 gmq是基于redis提供的特性,使用go语言开发的一个简单易用的队列;关于redis使用特性可以参考之前本人写过一篇很简陋的文章Redis 实现队列; gmq的灵感和设计是基于有赞延迟队 ...

  4. 从头学C语言——(1)编写一个简单的C程序

    编写一个简单的C程序 编译器安装完成之后,我们就可以用它来编写代码了. 这里用Dev C++作为示例,我的是5.11的版本(后续版本是其他人开发的分支版本). 1.双击打开软件, 2.点击左上角工具栏 ...

  5. 一箭穿心程序编码c语言,一个简单的一箭穿心程序

    一个简单的一箭穿心程序 其实这个程序也不难,只要用心形线方程(1-ρ×cosθ)来绘图就可以了. 作者:张跃华             学校:云南农业大学 程序如下: #include #includ ...

  6. c语言12之编程设计一个简单的计算器程序,要求根据用户从键盘输入的表达式:操作数1 运算符op 操作数2 计算表达式的值,指定的运算符为加减乘除。

    题目: 设计一个简单的计算器程序,要求根据用户从键盘输入的表达式: 操作数1 运算符op 操作数2 计算表达式的值,指定的运算符为加减乘除. 源代码: #include<stdio.h> ...

  7. python123程序设计题说句心里话_用c++写一个简单的计算器程序

    // 050305.cpp : 定义控制台应用程序的入口点. // // 050304.cpp : 定义控制台应用程序的入口点. // //四则运算 #include "stdafx.h&q ...

  8. 用java开发一个简单的安卓程序,Android NDK开发简单程序分享(Hello Word!)

    在之前的博客中已经为大家介绍了,如何在win环境下配置DNK程序,本篇我将带大家实现一个简单的Hello jni程序,让大家真正感受一下NDK开发的魅力.这里我们选择使用C+JAVA开发Android ...

  9. 如何编写一个简单的 Python 程序

    本教程将教你如何编写一个简单的 Python 程序.我们将从解释 Python 程序是什么开始,然后继续讨论语法的各个方面,即语言的结构. 什么是 Python? Python是一种越来越受欢迎的编程 ...

最新文章

  1. UOJ #274. 【清华集训2016】温暖会指引我们前行 [lct]
  2. 安装NUC972的BSP
  3. 程序员笔试面试基础知识资料整理
  4. buildroot--ubootkernelrootfs全编译工具
  5. ***CI新增记录成功后的返回值判断,是用isset还是empty
  6. 机器学习第二篇:详解KNN算法
  7. 织梦html权限设置,详细的Dede织梦目录权限安全设置教程
  8. 【Python】 注释
  9. Java代理模式学习
  10. 租房系统代码java_基于Java的租房管理系统的设计及实现.doc
  11. AtCoder Beginner Contest 171 A - αlphabet
  12. Git的commit your changes or stash them before you can merge
  13. KEIL4与KEIL5护眼配色方案
  14. 详解浪潮与Odoo联手背后 双方将带来哪些化学反应?
  15. 2021-11-09水洗碳带有什么特点
  16. [深入研究4G/5G/6G专题-40]: URLLC-11-《3GPP URLLC相关协议、规范、技术原理深度解读》-5-5G Qos原理与架构: 切片、PDU会话、QosFlow、5QI、DRB
  17. 计算机硬件工程师面试题集,硬件工程师笔试及面试问题
  18. Circular Billiard Table(计算几何)
  19. centOS7的vi中如何使用汉语拼音和五笔
  20. mipi传输距离3米_HDMI信号远距离如何实现传输?

热门文章

  1. php如何使用代码清除bom,使用php清除bom示例
  2. 高中计算机老师要教什么条件,应聘高中教师,到底需要什么学历?很多人都不知道!...
  3. 《深入理解Java虚拟机》笔记6——高效并发
  4. Spring AOP切点表达式详解
  5. 七大排序算法的个人总结(一)
  6. JVM源码—教你傻瓜式编译openjdk7
  7. 你必须学会的Git入门基本操作
  8. CTEX - 在线文档 - TeX/LaTeX 常用宏包
  9. CS231n课程笔记翻译:图像分类笔记(上)
  10. 新手学习编程的最佳方式是什么?