C语言实现:
成功编译的环境:Windows10 +vs2010
此程序转载自 http://www.cnblogs.com/xuwenmin888/archive/2013/05/04/3059282.html 并在作者的基础上进行了一定的修改 最后在自己的机子上成功运行
客户端的程序:
#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 <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文件中,然后编译即可。
再贴上服务端的程序:
#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 */
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文件。
在vs2010中直接运行程序不能成功 经过我的修改并且根据原作者的方法可以成功 在vs中运行会出现下列代码错误提示:Cannot find or open the PDB file 经过查找相关资料 得到了解决: 网上搜了半天找到解决的办法:
打开VS2010:工具--》选项--》》调试--》符号
接下来就是选择Microsoft,然后确认