Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1007939
  • 博文数量: 177
  • 博客积分: 3629
  • 博客等级: 中校
  • 技术积分: 1839
  • 用 户 组: 普通用户
  • 注册时间: 2005-02-23 21:21
文章分类

全部博文(177)

文章存档

2021年(1)

2020年(5)

2019年(4)

2018年(7)

2017年(1)

2016年(4)

2014年(1)

2013年(8)

2012年(10)

2011年(50)

2009年(12)

2008年(10)

2006年(56)

2005年(8)

分类: C/C++

2006-04-03 16:59:39

文件: tobaidu.rar
大小: 7KB
下载: 下载
新手之学习HTTP协议自力更生篇
工具:ethereal,vc6
文档:rfc1945(http1.0),rfc2614(http1.1)
平台:win2000 sp4/vc6 sp5
目的:
1  了解HTTTP协议
2  Windows Socket IO模式select应用
3 从理论到代码,得到一个网页.在本例中用的是的主页.
 
工具设置:
etheral菜单Capture->Options...->Capture Filter: 设为tcp and dst 220.181.27.5 or src 220.181.27.5  .还有Interface里要选择自己的网络接口.这样设的作用是只捕获含有指定IP的包.这里220.181.27.5是百度
 
关键代码如下:
 
int  main()
{
 WSADATA   wsaData;
 SOCKET       clientSocket;
 SOCKADDR_IN  servAddr;
 int        port=80,ret,once,count;
 fd_set       fdwrite,fdread;
 
 once=0;       //控制只发送一次
 char sended[]="GET / HTTP/1.1\r\n\
Accept: image/x=xbitmap,image/jpeg,image/pjpeg,application/x-shockwave-flash,\x2a\x2f\x2a\r\n\
Accept-Language: zh-cn\r\n\
Accept-Encoding: gzip,deflate\r\n\
User-Agent: Mozilla/4.0\r\n\
Host:
Connection: Keep-Alive\r\n\
\r\n";
 static char  recved[2048];      //会初始化为0
 WSAStartup(MAKEWORD(2,2),&wsaData);
 clientSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 servAddr.sin_family  = AF_INET;
 servAddr.sin_port   = htons(port);
     servAddr.sin_addr.s_addr = inet_addr("220.181.27.5");   //百度IP
  ret=connect(clientSocket,(SOCKADDR *)&servAddr,sizeof(servAddr));

/*如果只收发一次好象不需要循环,不过发送之后马上收不太好,还是等有了数据再收较好.这就是循环的作用*/
while(TRUE)         
{
  FD_ZERO(&fdwrite);
  FD_ZERO(&fdread);
  FD_SET(clientSocket,&fdwrite);
  FD_SET(clientSocket,&fdread);
  ret=select(0,&fdread,&fdwrite,NULL,NULL);
  if(ret==SOCKET_ERROR)
  {
   cout<<"SOCKET_ERROR\n"<  }
  if(ret>0)
  {
   if(FD_ISSET(clientSocket,&fdread))
   {
 //保证recved已初始化为0
    count=recv(clientSocket,recved,sizeof(recved),0);         cout<<"recved: "<     break;
   }
   if(FD_ISSET(clientSocket,&fdwrite))
   {
    if(once>0) continue; 
                //为了方便,这儿只用一个send代替了一大片保证发送完的代码,如果数据多的话,不要用一个send, 因为可能发送不完.
    int tmp=send(clientSocket,sended,sizeof(sended),0);
 cout<    once++;
   }
  }
}
 shutdown(clientSocket,SD_BOTH);
 closesocket(clientSocket);
 return 0;
}
关于构造的http头
char sended[]="GET / HTTP/1.1\r\n\
Accept: image/x=xbitmap,image/jpeg,image/pjpeg,application/x-shockwave-flash,\x2a\x2f\x2a\r\n\
Accept-Language: zh-cn\r\n\
Accept-Encoding: gzip,deflate\r\n\
User-Agent: Mozilla/4.0\r\n\
Host:
Connection: Keep-Alive\r\n\
\r\n";
其实这个头有几点最容易出错的:
GET / HTTP/1.1\r\n\
相互间隔一个空格,捕获的包里空格显示为0x20.而这里的\r\r表示换行回车,数据包里显示为0x0d,0x0a,在协议里表示为CRLF.
再说一下这一行最后的'\'字符,是把下面一行的和前面的连起来.在包里面显示为:
47 45 54 20 2f 20 48 54 54 50 2f 31 23 31 0d 0a 41 63 63 65 70 74 3a
这和
GET / HTTP/1.1..Accept:
是对应的,因为\r\n不可显,用..代替.
如果不知道URL,用偶的这样构造的头最小的头是:
char sended[]="GET / HTTP/1.1\r\n\
HOST:
\r\n";
可能有人奇怪,为什么最后一行又加上了\r\n,HOST字段不是有了么?呵呵,除了实体外,每个请求标题域都得加上0d0a尾,在C++中就是\r\n了.最后一个\r\n表示请求标题域到此为止,结束了.如果下面有主体的话,就可以开始了,下次再写写这个BODY.
select模式
凡是send,rec如果遇到socket接口出现一些错误会阻塞线程,比如如果一直没有数据可以接收,rec就会一直等,这不是好的方法.最好有一种机制,如果可以读了,你给我说一声,我再来读,这保证没有数据时不会让你再recv.
select就起到这种作用.当然IO模式很多,偶用select因为它简单并且应用广泛么.Sockets lib工程都是用的select模式,可见一般的应用够用了.还是因为偶不会别的,呵呵.
windows网络编程一书中有这种模式的一种描述,按步做就是.偶就略了.......
偶实践了一个,发现其实只能把select用到一个新线程中,用到主线程中也会影响反应.不过在新线程中运行一点不影响主线程的界面,挺好.
偶这个循环是在例子上改的.运行正常.下面是运行结果:
HTTP/1.1 200 OK
Date: Mon, 03 Apr 2006 08:30:03 GMT
Server: Apache/1.3.27
Set-Cookie: BAIDUID=FE7EF2E2C77F616D12E25B44BCA44584; expires=Mon, 03-Apr-36 08:
30:03 GMT; path=/; domain=.baidu.com
Cache-Control: max-age=86400
Expires: Tue, 04 Apr 2006 08:30:03 GMT
Last-Modified: Thu, 16 Mar 2006 10:15:00 GMT
ETag: "2d800e-b53-44193aa4"
Accept-Ranges: bytes
Content-Length: 2899
Connection: close
Content-Type: text/html
百度——全球最大中文搜索引擎  t-Type content="text/html; charset=gb2312">