一道c语言编程软件-编程

【C语言】【网络编程】菜鸟学习日志(三) 一个简易B/S系统——Http Server和精简的浏览器 - 宇坤 - 推酷
【C语言】【网络编程】菜鸟学习日志(三) 一个简易B/S系统——Http Server和精简的浏览器 - 宇坤
一个简易B/S系统——Http&Server和精简的浏览器
——在实战中开始C语言网络编程
写在前面:
这篇博文的题目同时也是我们这学期&C语言&课程中综合实验的题目。本菜鸟自然也是第一次干这事儿。在网上找了很多文章来学习,现在也写一篇自己的博文来回馈大家!
下面先简单介绍一下我们这篇博文要说些什么。
其实看题目就已经很明白了:就是要做一个Http&Server和一个精简的浏览器嘛。而为了实现这两个东西,我们大概要用到套接字(socket)和http协议相关的知识,其中包括URL的知识。
最后,我们要有一个browser程序,一个server程序。此外还有一个test.html文件作为传送的目标。
打开server让它运作;然后打开browser,输入URL,回车,请求就会生成报文(http协议)并发送给server。server接收到报文后,解析报文,并返回相应的应答报文。
不同于大多数文章上来就开始罗列相关函数(因为我是个菜鸟),我们不妨先通过一个简单的对比例子来了解一下我们整个B/S系统的通信过程。
比方说有一天你发短信给你的朋友,想问他/她索要一张近照。你的朋友收到短信后给你回复了一张前几天去海边玩的照片。那么在这个过程中,你和你的朋友就算是构成了一个B/S系统(浏览器/服务器系统)。在这个过程中你和朋友是如何实现通信的呢?
想想上面的例子,在展开讨论它之前,我们再来回忆一下平时用浏览器访问网页时的情景:
比如你要上百度,你会在浏览器的导航工具栏中输入:
,然后回车,浏览器就会把百度首页显示给你。什么?你直接去收藏栏里开百度?好吧,那只是浏览器自动帮你完成了填写网址的操作罢了。
事实上浏览器还帮你做了很多别的事,比如默认帮你补全前面的“http://”。
当我们要取得网络上的某个资源时,一般是通过各种途径给出了该资源的URL(统一资源定位符)。这是网络上每个资源所独有的地址。完整的格式如下:
传送协议://服务器:端口号/路径?查询
详细的介绍大家可以去文后的链接中找,本菜鸟就不赘述了。
网页的访问一般是通过http协议进行的。这种协议规定浏览器和服务器间通过规定格式的报文进行通信。通过协议,通信双方可以完全不用考虑对方通信的具体内部实现,只要能读懂报文(报文的格式是全球统一的)就可以通信了。也许你还没有理解这里的意思。没关系,这里我们先保留一个小惊喜,到后面你就会明白了。
再回过头看看我们找朋友要照片的例子。大概就是说,你要请求一个照片(资源),于是你填写了一封你们都可以理解的(遵守协议的)短信(报文),然后发送(send)给你的朋友;而另一边,你的朋友总是时刻在注意听手机有没有响(listen),当听到短信提示音后,便接收(accept)了你的短信并进行了阅读(read)。在明白了你的需求后,你的朋友写了一封附带照片的你们可以读懂的短信(应答报文)并回复给了你(send);然后你接收(recv)到了这个短信。这就是整个通信过程。当然在这些短信中,除了你们编写的文字外还附加了诸如发送时间、手机号码等附加属性。
我们浏览器和服务器之间的通信也是类似的。事实上,上面括号中给出的就是我们的程序中要用到的具体函数!
当然,与我们发短信还是有一些不同的地方,比如说——你可能已经听到过这个名字了——套接字。套接字在度娘那里是这样描述的:
多个TCP连接或多个应用程序进程可能需要通过同一个&TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。
更具体的定义和用法我在文后同样给出了链接。
到此为止,相信你已经对整个B/S系统有一定的认识了。那么接下来附上我的代码。根据我的注释,大家应该基本可以看懂了!
一个精简的浏览器。
可以接收用户输入的URL(ip形式),并进行访问
#include&&stdio.h&
#include&&stdlib.h&
#include&&string.h&
#include&&errno.h&
#include&&string.h&
#include&&sys/types.h&
#include&&netinet/in.h&
#include&&sys/socket.h&
#include&&sys/wait.h&
#define&MAXSIZE&2048
//URL解析器
int&analysis_URL(char&*buf,&char&*ip,&char&*port,&char&*html);
//报文生成器
int&packet_builder(char&*Header,&char&*html,&char&*ip,&char&*port);
int&main&(void)
int&i;&//用于计数
char&buf[MAXSIZE];&//存放URL
char&Header[MAXSIZE]&=&{'\0'};&//填写报文
char&s_ip[16];&//存放ip
char&s_p[5];&//存放端口号&字符串
unsigned&short&s_port&=&0;&//存放端口号&整形
char&s_html[30];&//存放路径
int&sockfd,&
struct&sockaddr_in&serv_
printf(&URL:\n&);
scanf(&%s&,buf);
if(!analysis_URL(buf,&s_ip,&s_p,&s_html))
s_port&=&0;
while(s_p[i]&!=&'\0')
s_port&=&s_port*10+(s_p[i++]-'0');
printf(&不支持的协议!&);
//创建套接字
if&((sockfd&=&socket(AF_INET,&SOCK_STREAM,&0))&==&-1)
perror(&socket&error!&);
//根据URL填写服务器端套接字网络地址信息
bzero(&serv_addr,sizeof(serv_addr));
serv_addr.sin_family&=&AF_INET;
serv_addr.sin_port&=&htons(s_port);
serv_addr.sin_addr.s_addr=&inet_addr(s_ip);
//与服务器端建立连接
if&(connect(sockfd,&(struct&sockaddr&*)&serv_addr,sizeof(struct&sockaddr))&==&-1)
perror(&connect&error!&);
//填写报文
memset(Header,&'\0',&MAXSIZE);&//先清空原来的报文
packet_builder(Header,&s_html,&s_ip,&s_p);
//发送请求报文
send(sockfd,&Header,&strlen(Header),0);
//接收应答文
if&((recvbytes&=&recv(sockfd,&buf,&MAXSIZE,0))&==&-1)&
perror(&recv&error!&);
buf[recvbytes]&=&'\0';
//打印接收到的响应
printf(&\n响应报文已接收!\n%s\n&,buf);
//关闭套接字
close(sockfd);
close(recvbytes);
#include&&stdio.h&
#include&&stdlib.h&
#include&&errno.h&
#include&&string.h&
#include&&sys/types.h&
#include&&netinet/in.h&
#include&&sys/socket.h&
#include&&sys/wait.h&
#include&&time.h&
#define&SERVPORT&3333&//用户接入端口
#define&BACKLOG&10&//允许等待连接数
#define&MAXSIZE&2048
//报文解析器
int&analysis_packet();
//应答报文头生成器
int&packet_builder_s();
int&main(void)&{
int&sockfd,client_
struct&sockaddr_in&my_
struct&sockaddr_in&remote_
char&msg[MAXSIZE]&=&{'\0'};
struct&tm&*
it=time(NULL);
//创建套接字
if&((sockfd&=&socket(AF_INET,SOCK_STREAM,0))&==&-1)
perror(&socket&create&failed!&);
//绑定端口地址
my_addr.sin_family&=&AF_INET;//通信类型
my_addr.sin_port&=&htons(SERVPORT);//端口
my_addr.sin_addr.s_addr&=&INADDR_ANY;//使用自己的ip地址
bzero(&(my_addr.sin_zero),8);
if&(bind(sockfd,&(struct&sockaddr*)&my_addr,&sizeof(struct&sockaddr))&==&-1)
perror(&bind&error!&);
//监听端口
if&(listen(sockfd,&BACKLOG)&==&-1)
perror(&listen&error&);
int&sin_size&=&sizeof(struct&sockaddr_in);
if&((client_fd&=&accept(sockfd,&(struct&sockaddr*)&remote_addr,&sin_size))&==&-1)
perror(&accept&error!&);
printf(&收到来自%s的连接!\n&,&(char*)inet_ntoa(remote_addr.sin_addr));
//子进程段
if&(!fork())
//接受client发送的请示信息
char&buf[MAXSIZE];
char&file[50]&=&{'\0'};
char&text[MAXSIZE];
char&fname[100]&=&&/home/arcane/C_learning&;&//服务器文件的路径
if&((rval&=&read(client_fd,&buf,&MAXSIZE))&&&0)
perror(&reading&stream&error!&);
printf(&%s\n&,buf);
//解析报文
if(!analysis_packet(buf,&file))&//解析成功
//生成本地文件路径
strcat(fname,&file);
if((fp=fopen(fname,&r&))&!=&NULL)&//找到文件
fseek(fp,&0,&SEEK_END);
size&=&ftell(fp);
fseek(fp,&0,&SEEK_SET);
//填写响应报文头
packet_builder_s(msg,&200,&size);
while(fgets(text,2048,fp)!=NULL)
strcat(msg,text);
fclose(fp);
//向client发送应答报文
if&(send(client_fd,&msg,&strlen(msg),&0)&==&-1)&perror(&send&error!&);
close(client_fd);
packet_builder_s(msg,&404,&0);
if&(send(client_fd,&msg,&strlen(msg),&0)&==&-1)&perror(&send&error!&);
close(client_fd);
printf(&报文解析失败!\n&);
close(client_fd);
结合之前的介绍,上面的代码是很好理解的。唯一的难点可能就在结构体&sockaddr_in&上面。这个结构体展开来是这样的:
struct&sockaddr_in&
short&int&sin_&//通信类型
unsigned&short&int&sin_&//&端口&
struct&in_addr&sin_&//&Internet&地址&
unsigned&char&sin_zero[8];&//&与sockaddr结构的长度相同
关于它的更多描述在文后链接中有提到,有兴趣深究的朋友可以看看。
当然,上面的代码你还不能直接使用。除了要把服务器程序中的资源路径改成你的资源路径外,我们还有4个函数只给出了声明而没有给出实现。它们分别是:browser的URL解析器和报文生成器、server的报文解析器和应答报文头生成器。而要实现这4个函数,我们就要深入了解http协议——因为我们的报文就是http协议规定的嘛!
报文的生成其实就是按照规则生成一个字符串,而报文解析就是对这个报文字符串按照同样的规则拆分的过程。报文中包含了诸如主机地址、请求资源、发送时间、接受状态等信息。
具体的格式我同样给出了链接。大家按照同样的规则写好的报文程序应该都是可以解析出来的。
下面我给出我的实现。像前面说的,我在这里只是实现了一小部分功能,比如browser只能生成GET类型的报文(这是最简单的类型,还有其他类型的可以在链接中找到)。但是它们确实是严格按照规则来的。
代码1(添加到browser.c底部):
URL一般格式:scheme://host:port/path?query#fragment
这是一个只能解析http协议的URL解析器
int&analysis_URL(char&*buf,&char&*ip,&char&*port,&char&*html)
if((p=strtok(buf,&&:&))&!=&NULL)
//确认为&http&协议
if(!strcmp(p,&&http&))
//确认&http&后的&&://&
p=strtok(NULL,&:&);
if(*p=='/'&&&&*(p+1)=='/')
strcpy(ip,&p+2);&//填写&ip
//确认有端口号
if((p=strtok(NULL,&:&))&!=&NULL)
if((i=strcspn(p,&/&))&&&strlen(p))
strncpy(port,&p,&i);&//填写&port
//端口号之后的&&/&
strcpy(html,&p+i);&//填写具体文件
printf(&缺少端口号!&);
printf(&不支持的协议!&);
printf(&格式错误!&);
报文生成器
这是一个只能生成GET类报文的简单生成器
报文首部只填写部分属性
int&packet_builder(char&*Header,&char&*html,&char&*ip,&char&*port)
strcat(Header,&GET&&);
strcat(Header,html);
strcat(Header,&&HTTP/1.1\r\n&);
strcat(Header,&Accept:&*/*\r\n&);
strcat(Header,&Accept-Language:&zh-cn\r\n&);
strcat(Header,&User-Agent:&Qbrowser/0.0\r\n&);
strcat(Header,&Host:&&);
strcat(Header,ip);
strcat(Header,&:&);
strcat(Header,port);
strcat(Header,&\r\nConnection:&Keep-Alive\r\n&);
strcat(Header,&\r\n&);
//请求主体
代码2(添加到server.c底部):
请求报文解析器
这是一个极简的解析器
事实上只解析了请求行
int&analysis_packet(char&*packet,&char&*f)
char&*delims={&&&\r&&};
if((p=strtok(packet,&delims))&==&NULL)
if(!strcmp(p,&GET&))&//确认请求为&GET
if((p=strtok(NULL,&delims))&==&NULL)
strcpy(f,&p);&//获取文件路径
if((p=strtok(NULL,&delims))&==&NULL)
if(!strcmp(p,&&HTTP/1.1&))&//确认协议为&HTTP/1.1
printf(&报文解析:未知的协议!\n&);
printf(&报文解析:未知的请求!\n&);
应答报文头生成器
只支持200和404状态
填写了部分属性
int&packet_builder_s(char&*msg,&int&status,&int&s)
char&num[5];
char&*wday[]&=&{&Sun&,&&Mon&,&&Tue&,&&Wed&,&&Thu&,&&Fri&,&&Sat&};
char&*wmon[]&=&{&Jan&,&&Feb&,&&Mar&,&&Apr&,&&May&,&&Jun&,&&Jul&,&&Aug&,&&Sep&,&&Oct&,&&Nov&,&&Dec&};
struct&tm&*
time(&it);
ptr=localtime(&it);&//取得本地时间
switch(status)
strcat(msg,&HTTP/1.1&200&OK\r\n&);
strcat(msg,&Date:&&);
strcat(msg,&wday[ptr-&tm_wday]);
strcat(msg,&&,&&);
sprintf(num,&%d&,ptr-&tm_mday);
strcat(msg,num);
strcat(msg,&&&);
strcat(msg,&wmon[ptr-&tm_mon]);
strcat(msg,&&&);
sprintf(num,&%d&,1900+ptr-&tm_year);
strcat(msg,num);
strcat(msg,&&&);
sprintf(num,&%d&,ptr-&tm_hour);
strcat(msg,num);
strcat(msg,&:&);
sprintf(num,&%d&,ptr-&tm_min);
strcat(msg,num);
strcat(msg,&:&);
sprintf(num,&%d&,ptr-&tm_sec);
strcat(msg,num);
strcat(msg,&&GMT\r\n&);
strcat(msg,&Content-Type:&text/charset=gb2312\r\n&);
strcat(msg,&Content-Length:&&);
sprintf(num,&&%d&,&s);
strcat(msg,&num);
strcat(msg,&\r\n\r\n&);
strcat(msg,&HTTP/1.1&404&Not&Found\r\n&);
strcat(msg,&Date:&&);
strcat(msg,&wday[ptr-&tm_wday]);
strcat(msg,&&,&&);
sprintf(num,&%d&,ptr-&tm_mday);
strcat(msg,num);
strcat(msg,&&&);
strcat(msg,&wmon[ptr-&tm_mon]);
strcat(msg,&&&);
sprintf(num,&%d&,1900+ptr-&tm_year);
strcat(msg,num);
strcat(msg,&&&);
sprintf(num,&%d&,ptr-&tm_hour);
strcat(msg,num);
strcat(msg,&:&);
sprintf(num,&%d&,ptr-&tm_min);
strcat(msg,num);
strcat(msg,&:&);
sprintf(num,&%d&,ptr-&tm_sec);
strcat(msg,num);
strcat(msg,&&GMT\r\n\r\n&);
上面的代码只是一个小小的示范。相信在你充分理解了http协议和一些string操作的方法之后,你可以写出更强大、更稳定的函数!
记得之前说过要把server.c中的服务器资源路径改成你的路径吗?我们在这个路径下放一个用于测试的test.html文件。不然空空如也的服务器象什么话嘛!这个文件是用html语言写的,不用管它,复制粘贴下面的内容就好了!
&meta&http-equiv=&Content-Type&&content=&text/&charset=gb2312&&/&
&meta&http-equiv=&Content-Language&&content=&zh-cn&&/&
&body&bgcolor=&yellow&&
&h2&Hello&World&/h2&
这样服务器就有资源了。服务器每次受到请求,都会在服务器资源路径下查找,如果找到了,就返回200报文,并附上文件内容;如果没找到,则返回404报文。
下面来看看我们的成果吧!
把我们的程序编译好。
运行server,它就开始监听连接了。保持它的运行。
运行browser,输入:
你就会看到服务器和浏览器都收到了对方发来的报文!
在browser中输入:
由于我们没有在资源路径下放置&no.html&这个文件,于是浏览器会接收到一个404报文。
虽然很简单,但是以上就是我们的精简B/S系统的全部了!
哦,等等!还有一个说好的小惊喜呢!
之前我们有说过的,由于我们是严格按照http协议来编写的程序,所以它可以和任何其他同样遵循此协议的程序实现通信而无需考虑其内部实现!什么意思呢?
现在,保持你的server运行。
打开你的浏览器。注意,不是那个browser,而是你的系统浏览器,比如火狐。
在你的导航栏里——不要怀疑——输入:
你会发现你的浏览器真的为你打开了一个网页!这个网页是黄色的页面,上面写这一行字:“Hello&World!”
你也许会很奇怪,你完全不知道火狐是如何编写的,但是它却可以访问你的服务器!其实也不必大惊小怪,所谓协议就是用来干这个的呀!
这篇文章提供了详细的c语言socket函数讲解,很多定义和用法都可以找到:
http://blog.csdn.net/shisqf/article/details/6563942
这篇文章比较直观地给出了http协议的格式和例子:
/shaoge/archive//1546019.html
这篇文章更详细地介绍了http的相关概念,包括URL的介绍:
/content/13/44.shtml
这篇文章是一个介绍操作string的各种函数的好文章,在报文的生成和解析中可能用得到:
http://blog.csdn.net/sunnylgz/article/details/6677103
已发表评论数()
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
没有分页内容
图片无法显示
视频无法显示
与原文不一致> 一道C语言编程题目,有关组合两个乒乓球赛进行比赛,各出3人,甲队为A,B,C3人,乙队为X,Y,Z
一道C语言编程题目,有关组合两个乒乓球赛进行比赛,各出3人,甲队为A,B,C3人,乙队为X,Y,Z
发布时间: & &
浏览:155 & &
回复:2 & &
悬赏:0.0希赛币
一道C语言编程题目,有关组合两个乒乓球赛进行比赛,各出3人,甲队为A,B,C3人,乙队为X,Y,Z3人,已经抽签决定比赛名单,有人向队员打听比赛名单,A说他不和X比,C说他不和X,Z比,请编程序找出3对赛手的名单。
请问该如何编写,谢谢!
int main(int argc, char *argv[])
char a,b,c;
for (a= 'x ';a &= 'z ';a++){
for (b= 'x ';b &= 'z ';b++){
if (b!=a){
for (c= 'x ';c &= 'z ';c++)
if (c!=b && c!=a){
if (a!= 'x ' && c!= 'x ' && c!= 'z '){
printf( &a v.s %c\t b v.s %c\t c v.s %c\t\n &,a,b,c);
system( &pause &);
& & (0)(0)procedure TForm8.Button25Click(Sender: TObject);
'a ' to
'c ' do
'x ' to
'z ' do
'a ')and(b =
'z '))or((a =
'c ')and((b =
'x ')or(b =
'z '))) then continue
listbox1.Items.Add(String(a)+String(b)); wkq777 & &
& & (0)(0)
本问题标题:
本问题地址:
温馨提示:本问题已经关闭,不能解答。
暂无合适的专家
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&以下是参考&winsock网络编程经络&中讲解web应用http协议的时候,实现的一个简单的http程序,包含一个服务器和一个客户端。
先贴上客户端的程序:
/*************************************************************************
* Copyright (c)
by xuwm All Rights Reserved
* FILENAME:
* PURPOSE :
HTTP 客户端程序, 获取网页.
**************************************************************************/
#include "stdafx.h"
#include &stdio.h&
#include &winsock2.h&
#pragma comment(lib, "ws2_32.lib")
/* WinSock使用的库函数 */
/* 定义常量 */
#define HTTP_DEF_PORT
/* 连接的缺省端口 */
#define HTTP_BUF_SIZE
/* 缓冲区的大小
#define HTTP_HOST_LEN
/* 主机名长度 */
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";
/**************************************************************************
* 函数功能: 解析命令行参数, 分别得到主机名, 端口号和文件名. 命令行格式:
[: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, *
/* 查找主机的开始位置 */
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_
/* 得到文件名 */
file = strrchr(host_end, '/');
if (file && (file + 1) != buf_end)
strcpy(file_name, file + 1);
if (colon) /* 得到端口号 */
length = host_end -
memcpy(port_buf, colon, length);
port_buf[length] = 0;
*port = atoi(port_buf);
host_end = colon - 1;
/* 得到主机信息 */
length = host_end -
memcpy(host, begin, length);
host[length] = 0;
int main(int argc, char **argv)
WSADATA wsa_
http_sock = 0;
/* socket 句柄 */
struct sockaddr_in serv_
/* 服务器地址 */
struct hostent *host_
int result = 0, send_
char data_buf[HTTP_BUF_SIZE];
char host[HTTP_HOST_LEN] = "127.0.0.1";
unsigned short port = HTTP_DEF_PORT;
char file_name[HTTP_HOST_LEN] = "index.html";
char file_nameforsave[HTTP_HOST_LEN] = "index1.html";
FILE *file_
if (argc != 2)
printf("[Web] input : %s [: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 =
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();
&首先在vs2010中的,添加一个VC命令行程序,把上面的程序直接放到主程序对应的cpp文件中,然后编译即可。
再贴上服务端的程序:
/*************************************************************************
* Copyright (c)
by xuwm All Rights Reserved
* FILENAME:
* PURPOSE :
HTTP 服务器程序, 向客户端提供请求的文件内容.
**************************************************************************/
#include "stdafx.h"
#include &stdio.h&
#include &winsock2.h&
#pragma comment(lib, "ws2_32.lib")
/* WinSock使用的库函数 */
/* 定义常量 */
#define HTTP_DEF_PORT
/* 连接的缺省端口 */
#define HTTP_BUF_SIZE
/* 缓冲区的大小 */
#define HTTP_FILENAME_LEN
/* 文件名长度 */
/* 定义文件类型对应的 Content-Type */
struct doc_type
char * /* 文件后缀 */
/* Content-Type */
struct doc_type file_type[] =
"text/html"
"image/gif"
"image/jpeg" },
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 *
for (type = file_ type-& type++)
if (strcmp(type-&suffix, suffix) == 0)
return type-&
return NULL;
/**************************************************************************
* 函数功能: 解析请求行, 得到文件名及其后缀. 请求行格式:
[GET :8080/index.html HTTP/1.1]
* 参数说明: [IN]
buf, 字符串指针数组;
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, *
/* 查找 URL 的开始位置 */
begin = strchr(buf, ' ');
begin += 1;
/* 查找 URL 的结束位置 */
end = strchr(begin, ' ');
bias = strrchr(begin, '/');
length = end -
/* 找到文件名的开始位置 */
if ((*bias == '/') || (*bias == '\\'))
/* 得到文件名 */
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, 字符串指针数组;
buf_len, buf 的长度;
* 返 回 值: 成功返回非0, 失败返回0.
**************************************************************************/
int http_send_response(SOCKET soc, char *buf, int buf_len)
int read_len, file_len, hdr_len, send_
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_
/* 得到文件名和后缀 */
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);
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");
/* 构造 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());
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_
} while ((read_len & 0) && (file_len & 0));
fclose(res_file);
int main(int argc, char **argv)
WSADATA wsa_
SOCKET srv_soc = 0, acpt_
/* socket 句柄 */
struct sockaddr_in serv_
/* 服务器地址
struct sockaddr_in from_
/* 客户端地址
char recv_buf[HTTP_BUF_SIZE];
unsigned short port = HTTP_DEF_PORT;
int from_len = sizeof(from_addr);
int result = 0, recv_
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");
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());
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());
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");
&这个也跟客户端程序一样,打开VS2010,新建一个VC命令行程序,COPY上面的代码,直接放到主程序的CPP文件中,编译即可。
运行代码如下:
1.先运行服务端程序,绑定端口,然后开启监听& 在CMD里先切换到exe的目录,然后 输入 服务端程序名.exe 9000,此处服务端程序名换成对应的程序名称.后面的9000端口号,也可以换成别的。
&2. 再运行客户羰程序,同上面一样,切换到exe&的目录,然后输入 客户端程序名.exe ,& 此处客户端程序名换成对应的程序名称,后面的,代表请求的网页路径。
&3. 在服务器的exe目录下,应创建一个index.html文件,里面可以输入一个正规的html文件。
以上只是学习网络编程的一点小体会,尽当以后温故:)
阅读(...) 评论()}

我要回帖

更多关于 c语言小游戏编程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信