分类: 2006-01-25 21:32 1367人阅读 (0)
#include <stdio.h>
#include <stdlib.h>#include <errno.h>#include <string.h>#include <netdb.h>#include <unistd.h>#include <fcntl.h>#include <fcntl.h>#include <sys/time.h>#include <sys/types.h>#include <netinet/in.h>#include <sys/socket.h>#define MaxRecvLen 500//定义网站结构
struct DOMAIN{ char url[255]; //http://www.xh88.com:80/index.php 完整网址 char host[20]; //www.xh88.com 主机地址 int port; //sock 联接的端口号};typedef struct DOMAIN domain;int main(int argc,char *argv[])
{//******************* 有关 sock 的变量 *************************
int sock_id; //sock 联接句柄
struct hostent *hostbyname; //主机的信息 结构
struct sockaddr_in remote_addr; //包含远端主机IP地址和端口号的结构 //******************* 有关 无阻塞 select 所用到 的变量 *************************struct timeval timeout; //用于无阻塞 select 调用 的时间变量
fd_set sockid_readable; //sock 类似联接句柄 传递给 select 有效的连接domain web_domain; //定义一个 web domain 的结构
char send_str[255]; //保存要发送的字符串
char *tempStr,*recBuf; //临时字串,接收到的字串指针int recv_numb; //保存远端返回的字串大小
FILE *fp; //文件指针,用于保存从远端得到的数据 char *tmpfile; //临时文件名timeout.tv_sec = 1; //设置几秒钟超时
timeout.tv_usec = 500000; //设置几微秒超时 web_domain.port=80; //默认端口 为 80tmpfile="abc.txt";
//******************* 处理命令行的参数 *************************if(argc<2) //参数不足
{ printf("%s","/n/nExp: ./client /n/n"); exit(1); } if(!(tempStr=strstr(argv[1],"http://"))) //必须以 http://开头 { printf("parameter error!/n"); exit(0); } strcpy(web_domain.url,tempStr); //http://www.xh88.com:80/index.php strcpy(web_domain.host,web_domain.url+7); //www.xh88.com //处理 host if((tempStr=strstr(web_domain.host,"/"))) { *tempStr='/0'; //web_domain.host 里去除:后的东西 } if((tempStr=strstr(web_domain.host,":"))) { *tempStr='/0'; //Host 里去除:后的东西 web_domain.port=atoi(tempStr+1); //改写端口 }//!printf("%s/n%d/n%s/n",web_domain.host,web_domain.port,web_domain.url);hostbyname = gethostbyname(web_domain.host); //将基本名字和地址转换
if((sock_id=socket(PF_INET,SOCK_STREAM,0))==-1) { perror("socket"); exit(1); }//初始化远端要传递的socket
remote_addr.sin_family=AF_INET; //地址族 remote_addr.sin_port=htons(web_domain.port); //端口号 remote_addr.sin_addr=*((struct in_addr *)hostbyname->h_addr); //IP地址 bzero(&(remote_addr.sin_zero),8); //填充0 以保持与struct sockaddr同样大小//和服务器建立连接
if(connect(sock_id,(struct sockaddr *)&remote_addr,sizeof(remote_addr))==-1) { perror("connect"); exit(1); }printf("/n/nconnected %s:%d ok!/n/n",web_domain.url,web_domain.port);//创建要发送给远端的字串
sprintf(send_str,"GET %s HTTP/1.1/nAccept: */*/nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)/nHost: %s/n/n",web_domain.url,web_domain.host); printf("%s/n/n",send_str);//开始向服务器发送数据 if(send(sock_id,send_str,strlen(send_str),0)==-1) { perror("send"); exit(1); } recBuf=malloc(MaxRecvLen); //为储存远端返回的字串的变量申请空间 //fp=fopen(tmpfile,"w"); while(1) {//设置无堵塞模式
FD_ZERO(&sockid_readable); FD_SET(sock_id,&sockid_readable); select(sock_id+1, &sockid_readable, NULL, NULL, &timeout); if (!FD_ISSET(sock_id, &sockid_readable)) { printf("Timed out./n"); break; } recv_numb = recv(sock_id,recBuf,MaxRecvLen,0); //得到远端返回的字串 recBuf[recv_numb]='/0';//fputs(fp,);
// fp=fopen(tmpfile,"w"); printf("/n/t********* %d *******",recv_numb); printf("%s",recBuf); if(recv_numb<1) //返回的长度不够退出循环 break; }printf("/n/nrecv ok!/n/n");
close(sock_id); return 0;}socket下载网页时网络阻塞
2006-9-25 00:00
提问者: | 悬赏分:20 | 浏览次数:1735次 我用socket写了个小程序,用来下载一个网页的HTML文件,connect和connect以前都没有问题,当recv的返回值是0的时候,就显示下载完毕.我用它来下mail.163.com的HTML的时候,什么问题也没有,但如果用来下zhidao.baidu.com的HTML的时候,明明整个HTML都下载下来了,可它却不马上显示下载完毕,停在recv(),要过一些时间才显示下载完毕.哪位大虾给解释一下啊.它在停下的这一些时间里做什么,以及怎么避免这种情况发生.没时间大体给分析一下也可以啊.
| 2006-10-1 14:59
最佳答案 这个问题涉及到HTTP协议。估计zhidao默认是以KeepAlive的方式处理HTTP请求的,就是说服务器端会保持连接一段时间,以便重新利用该连接接受新的HTTP请求。解决的办法是 1. 发送HTTP请求的时候主动要求不使用KeepAlive;2. 或者对服务器返回的内容做分析,根据返回的头部信息能够知道数据的长度或者数据块的大小和数目(如果是以Transfer-Encoding: chunked的方式返回),这样就能确定是否已经接受了所有的数据,然后进行主动close().另外补充一下: 自己写socket程序下载网页会涉及到很多问题,比如有些服务器是把数据压缩后返回的,比如google的有些服务,你还需要对它进行解压还原。因此,建议使用一些已有的库进行开发会比较方便一点,推荐使用libcurl来处理HTTP/FTP。