以下是参考中讲解web应用http协议的时候,实现的一个简单的http程序,包含一个服务器和一个客户端。
先贴上客户端的程序:
/*************************************************************************** Copyright (c) 2012-2013 by xuwm All Rights Reserved** FILENAME: WebClnt.c** PURPOSE : HTTP 客户端程序, 获取网页.** AUTHOR : 许文敏***************************************************************************/#include "stdafx.h"#include#include#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 \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.***************************************************************************/voidhttp_parse_request_url(constchar*buf,char*host,unsignedshort*port,char*file_name){intlength = 0;charport_buf[8];char*buf_end = (char*)(buf +strlen(buf));char*begin, *host_end, *colon, *file;/* 查找主机的开始位置 */begin =const_cast(strstr(buf,"//"));begin = (begin ? begin + 2 :const_cast(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;}intmain(intargc,char**argv){WSADATA wsa_data;SOCKET http_sock = 0;/* socket 句柄 */structsockaddr_in serv_addr;/* 服务器地址 */structhostent *host_ent;intresult = 0, send_len;chardata_buf[HTTP_BUF_SIZE];charhost[HTTP_HOST_LEN] ="127.0.0.1";unsignedshortport = HTTP_DEF_PORT;unsignedlongaddr;charfile_name[HTTP_HOST_LEN] ="index.html";charfile_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, (structsockaddr *) &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();return0;}
首先在vs2010中的,添加一个VC命令行程序,把上面的程序直接放到主程序对应的cpp文件中,然后编译即可。
再贴上服务端的程序:
/*************************************************************************** Copyright (c) 2012-2013 by xuwm All Rights Reserved** FILENAME: WebSrv.c** PURPOSE : HTTP 服务器程序, 向客户端提供请求的文件内容.** AUTHOR : 许文敏***************************************************************************/#include "stdafx.h"#include#include#pragma comment(lib, "ws2_32.lib") /* WinSock使用的库函数 *//* 定义常量 */#define HTTP_DEF_PORT 80 /* 连接的缺省端口 */#define HTTP_BUF_SIZE 1024 /* 缓冲区的大小 */#define HTTP_FILENAME_LEN 256 /* 文件名长度 *//* 定义文件类型对应的 Content-Type */structdoc_type{char*suffix;/* 文件后缀 */char*type;/* Content-Type */};structdoc_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 \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(constchar*suffix){structdoc_type *type;for(type = file_type; type->suffix; type++){if(strcmp(type->suffix, suffix) == 0)returntype->type;}returnNULL;}/**************************************************************************** 函数功能: 解析请求行, 得到文件名及其后缀. 请求行格式:* [GET http://www.baidu.com:8080/index.html HTTP/1.1]** 参数说明: [IN] buf, 字符串指针数组;* [IN] buflen, buf 的长度;* [OUT] file_name, 文件名;* [OUT] suffix, 文件名后缀;** 返 回 值: void.***************************************************************************/voidhttp_parse_request_cmd(char*buf,intbuflen,char*file_name,char*suffix){intlength = 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.***************************************************************************/inthttp_send_response(SOCKET soc,char*buf,intbuf_len){intread_len, file_len, hdr_len, send_len;char*type;charread_buf[HTTP_BUF_SIZE];charhttp_header[HTTP_BUF_SIZE];charfile_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);return0;}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");return0;}/* 构造 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());return0;}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);return1;}intmain(intargc,char**argv){WSADATA wsa_data;SOCKET srv_soc = 0, acpt_soc;/* socket 句柄 */structsockaddr_in serv_addr;/* 服务器地址 */structsockaddr_in from_addr;/* 客户端地址 */charrecv_buf[HTTP_BUF_SIZE];unsignedshortport = HTTP_DEF_PORT;intfrom_len =sizeof(from_addr);intresult = 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, (structsockaddr *) &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, (structsockaddr *) &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");return0;}这个也跟客户端程序一样,打开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程序
关注
打赏
