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

http://www.cnblogs.com/xuwenmin888/archive/2013/05/04/3059282.html


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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C语言实现HTTP协议传输程序需要遵循HTTP协议的规则,并使用套接字编程实现网络通信。这是一个简单的例子: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netdb.h> int main(int argc, char *argv[]) { int sockfd, bytes; char buffer[1024]; struct sockaddr_in server_addr; struct hostent *host; if (argc < 2) { printf("Usage: %s hostname\n", argv[0]); exit(1); } host = gethostbyname(argv[1]); if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(80); server_addr.sin_addr = *((struct in_addr *)host->h_addr); bzero(&(server_addr.sin_zero), 8); if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { perror("connect"); exit(1); } sprintf(buffer, "GET / HTTP/1.1\r\n\r\n"); send(sockfd, buffer, strlen(buffer), 0); while ((bytes = recv(sockfd, buffer, 1024, 0)) > 0) { buffer[bytes] = '\0'; printf("%s", buffer); } close(sockfd); return 0; } ``` 这个程序通过主机名发送一个GET请求,并打印出响应的内容。 ### 回答2: 要实现一个基本的HTTP协议传输程序,你可以按以下步骤进行: 1. 导入所需的头文件:`stdio.h`, `stdlib.h`, `string.h`, `sys/socket.h`, `netinet/in.h` 2. 创建一个套接字来连接到服务器,并检查是否成功创建。 ```c int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { printf("套接字创建失败\n"); exit(1); } ``` 3. 设置服务器地址和端口,并使用 `connect()` 函数连接到服务器。 ```c struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(80); server_addr.sin_addr.s_addr = inet_addr("服务器IP地址"); if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { printf("连接服务器失败\n"); exit(1); } ``` 4. 创建HTTP请求并发送到服务器。你可以使用 `send()` 函数发送请求。 ```c char request[1024] = "GET / HTTP/1.1\r\nHost: 服务器IP地址\r\nConnection: close\r\n\r\n"; if (send(sockfd, request, strlen(request), 0) == -1) { printf("发送请求失败\n"); exit(1); } ``` 5. 从服务器接收响应并打印出来。你可以使用 `recv()` 函数接收响应。 ```c char response[4096]; memset(response, 0, sizeof(response)); if (recv(sockfd, response, sizeof(response), 0) == -1) { printf("接收响应失败\n"); exit(1); } printf("%s\n", response); ``` 6. 关闭套接字连接。 ```c close(sockfd); ``` 这样,你就实现了一个简单HTTP协议传输程序。当然,这只是一个基本的示例,你可以根据实际需求添加更多的功能,如处理响应状态码、解析响应头等。 ### 回答3: 实现一个基本的HTTP协议传输程序涉及到建立TCP连接、发送HTTP请求、接收HTTP响应等步骤。以下是一个简单的C语言实现示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netdb.h> #define MAX_BUFFER_SIZE 1024 int main() { int sockfd; char buffer[MAX_BUFFER_SIZE]; char request[MAX_BUFFER_SIZE]; // 创建套接字 sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror("Error creating socket"); exit(1); } // 设置服务器信息 struct sockaddr_in server; server.sin_family = AF_INET; server.sin_port = htons(80); struct hostent *host; host = gethostbyname("www.example.com"); server.sin_addr = *((struct in_addr *)host->h_addr); // 建立TCP连接 if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1) { perror("Error connecting to server"); exit(1); } // 构建HTTP请求 snprintf(request, sizeof(request), "GET / HTTP/1.1\r\n" "Host: www.example.com\r\n" "Connection: close\r\n" "\r\n"); // 发送HTTP请求 if (send(sockfd, request, strlen(request), 0) == -1) { perror("Error sending request"); exit(1); } // 接收HTTP响应 ssize_t received; while ((received = recv(sockfd, buffer, sizeof(buffer) - 1, 0)) > 0) { buffer[received] = '\0'; printf("%s", buffer); } // 清理套接字 close(sockfd); return 0; } ``` 以上示例代码实现了一个简单HTTP协议传输程序。它使用了C语言的socket库函数,通过建立TCP连接、发送HTTP请求以及接收HTTP响应来实现与服务器的通信,并将接收到的响应打印到控制台上。 请注意,该示例仅用于演示目的,实际的HTTP协议传输程序可能需要更多的错误处理、协议解析和其他功能来实现完整的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值