http客户端如何写
使用wireshark协助,设置网卡本地,设置过滤器:http && (ip.src == 192.168.1.80 && ip.dst == 192.168.1.81) || (ip.src == 192.168.1.81 && ip.dst == 192.168.1.80)。
查看数据:
接收数据:
Content-Length:17364652代表所发的数据字节总数,在\r\n\r\n后即是数据的开始处。
数据为:LOAD为文件的魔术字 表示文件的一个唯一值,可以将分割的块聚在一起。
时间
有限搬代码:
int Get_File(int sockfd,char *url,int filelength,char *filebuf) { int count; char range[32]; int i; int ret = 0; char *p = NULL; int downOver=0; int downlength=0,writeRet; int p_file; int s32TimeOut=5*1000; fd_set rfds; int nRecvBuf=64*1024;//设置为32K struct timeval tv; count = (filelength%MAX_RECV_SIZE)?(filelength/MAX_RECV_SIZE +1):(filelength/MAX_RECV_SIZE); for(i=0;i<1;i++) { if((i == (count-1))&&(filelength%MAX_RECV_SIZE)) sprintf(range,"%d-%d",i*MAX_RECV_SIZE,filelength-1); else sprintf(range,"%d-%d",i*MAX_RECV_SIZE,(i+1)*MAX_RECV_SIZE-1); Package_Url_Get_File(url,range); printf("send = %s \n",g_buf_send); Send(sockfd, g_buf_send, strlen(g_buf_send), 0); Net_Http_GetResponseHeader(sockfd); /*需改为提取http 返回协议头和协议体总长,然后定长接收*/ //setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char*)s32TimeOut,sizeof(s32TimeOut)); //setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,(char*)nRecvBuf,sizeof(nRecvBuf)); while(!downOver) { re_recv: tv.tv_sec = 5; tv.tv_usec = 0; FD_ZERO(&rfds); //每次循环都要清空集合,否则不能检测描述符变化 FD_SET(sockfd, &rfds); //添加描述符 memset(g_buf_recv,0x0,sizeof(g_buf_recv)); switch (select(sockfd + 1, &rfds, NULL, NULL, &tv)) { case 1: { usleep(100); ret = Recv(sockfd,g_buf_recv,sizeof(g_buf_recv)-1,0); s32TimeOut = 0; printf("recv len = %d\n", ret); if ( 0 > ret ) { printf("recvfrom error\n"); break; } p=g_buf_recv; Write_File(p,"",ret); printf("strlen(p)=%d \n",strlen(p)); if(ret > 0) downlength += ret; if(downlength >= filelength) downOver = 1; goto re_recv; } case 0: { printf("timeout\n");//再次轮询 s32TimeOut--; if (s32TimeOut == 0) { printf("last timeout\n"); } break; } default: printf("Failed to select\n");//select错误,退出程序 } } } return 0; }
void Package_Url_Get_File(char *url, char *range) { char buf_range[64]; memset(g_buf_send,0x0,sizeof(g_buf_send)); sprintf(g_buf_send, "GET %s",url); //HTTP/1.1\r\n 前面需要一个空格 strcat(g_buf_send," HTTP/1.1\r\n"); strcat(g_buf_send, "Host: "); strcat(g_buf_send, IP); strcat(g_buf_send, ":"); strcat(g_buf_send, PORT); // sprintf(buf_range, "\r\nRange: bytes=%s\r\n",range); // strcat(g_buf_send,buf_range); strcat(g_buf_send,"\r\nConnection: Keep-Alive\r\n\r\n"); }
int Send(int sockfd, char *sendbuf, int len, int flags) { int sendlen = 0; int ret = -1; while(sendlen < len) { ret = send(sockfd, sendbuf+sendlen, len-sendlen, flags); if(-1 == ret) { perror("send"); exit(EXIT_FAILURE); } else sendlen += ret; } return 0; }
int Recv(int sockfd, char *recvbuf, int len, int flags) { int recv_len; if ((recv_len = recv(sockfd, recvbuf, len, flags)) < 0) { perror("recv error"); exit(EXIT_FAILURE); } return recv_len; }
char* Net_Http_GetResponseHeader(int sockfd) { char c = 0; int nIndex = 0; static char ResponseHeader[1024]={0}; int bEndResponse = 0; while(!bEndResponse && nIndex < 1024) { recv(sockfd,&c,1,0); ResponseHeader[nIndex++] = c; if(nIndex >= 4) { if(ResponseHeader[0]!='H'&&ResponseHeader[1]!='T' &&ResponseHeader[2]!='T'&&ResponseHeader[3]!='P') { printf("ResponseHeader error !\n"); return NULL; } if(ResponseHeader[nIndex - 4] == '\r' && ResponseHeader[nIndex - 3] == '\n' && ResponseHeader[nIndex - 2] == '\r' && ResponseHeader[nIndex - 1] == '\n') bEndResponse = 1; } } ResponseHeader[nIndex]=0; printf("ResponseHeader:%s \n",ResponseHeader); return ResponseHeader; }
假如要实现断点则只需要加入字段Range: bytes=%d- (%d)是你需要开始下载的文件的起始字节数。
1.在linux 在写代码过程中要注意写入文件时候,写入的字节数要是recv函数返回的值为准,不要使用strlen函数,在返回的包中,尽量先将包头过滤出来。
2.在window下,打开文件函数fopen需要指定wb二进制方式打开,否则在windows下,fwrite函数写入数据时会加上回车符OX0D。
3.在window下,调试时候,如果在调用fwrite函数写入,没有经过fclose,ctrl+c程序终止的话会造成最后一段数据没有真正写入到文件。