扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
本文参考Linux C/C++ 开发(学习笔记十 ):实现http请求器(TCP客户端)_菊头蝙蝠的博客-博客_linux c++ http客户端
超过十余年行业经验,技术领先,服务至上的经营模式,全靠网络和口碑获得客户,为自己降低成本,也就是为客户降低成本。到目前业务范围包括了:成都网站设计、成都网站建设,成都网站推广,成都网站优化,整体网络托管,成都微信小程序,微信开发,App定制开发,同时也可以让客户的网站和网络营销和我们一样获得订单和生意!一、需要实现的内容 客户端请求消息 客户端发送一个 HTTP 请求到服务器的请求消息包括以下格式:请求行( request line )、请求 头部( header )、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。 服务器响应消息 HTTP 响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。 下面实例是一点典型的使用 GET 来传递数据的实例: 客户端请求:GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi
服务端响应:
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain
二、全部代码#include#include#include#include#include#include
#include#include#include#define HTTP_VERSION "HTTP/1.1"
#define CONNECTION_TYPE "Connection:close\r\n"
#define BUFFER_SIZE 4096
char* host_to_ip(const char* hostname){
struct hostent *host_entry=gethostbyname(hostname);
//14.215.177.39 -->usigned int(因为ipv4是32位的,每个点分隔,值为0~255.正好可以用unsigned int表示,每8位表示一个0~255,共4个)
//inet_nota 是把 0x12121212 --->"18.18.18.18" 也就是将网络地址转换成“.”点隔的字符串格式
if(host_entry){
return inet_ntoa(*(struct in_addr*)*host_entry->h_addr_list);//由于h_addr_list可能有几个ip地址,这边取第一个,因此加*,然后强转成in_addr*,由于inet_ntoa为值传递,因此还要加*
}
return NULL;
}
int http_create_socket(char* ip){
int sockfd=socket(AF_INET,SOCK_STREAM,0);//sockfd的结果就是int类型,是一个文件句柄。TCP要用SOCK_STREAM,UDP用SOCK_DGRAM
struct sockaddr_in sin={0};
sin.sin_family=AF_INET;
sin.sin_port=htons(80);//http协议端口为80
sin.sin_addr.s_addr=inet_addr(ip);//"字符串的ip"地址转为uint32_t和上面的inet_ntoa正好相反
// int ret=connect(sockfd,(sockaddr*)&sin,sizeof(sockaddr_in));
// if(ret!=0) return -1;
if (0 != connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in))) {
return -1;
}
fcntl(sockfd,F_SETFL,O_NONBLOCK);//设置为非阻塞(如果socket是阻塞,read(),会阻塞挂起。如果socket是非阻塞,read()也是立刻返回)(设置文件状态标志用F_GETFL,读取文件状态标志用F_SETFL)
return sockfd;
}
//hostname:github.com
//resource:/wangbojing
char* http_send_request(const char* hostname,const char* resource){
char* ip=host_to_ip(hostname);
int sockfd=http_create_socket(ip);
char buffer[BUFFER_SIZE]={0};
sprintf(buffer,
"GET %s %s\r\n\
Host:%s\r\n\
%s\r\n\
\r\n",
resource,HTTP_VERSION,
hostname,
CONNECTION_TYPE
);
send(sockfd,buffer,strlen(buffer),0);
// recv()//因为是非阻塞状态,如果用recv会快速过去
//select
fd_set fdread;
FD_ZERO(&fdread);//用来清空fd_set集合,即让fd_set集合不再包含任何文件句柄
FD_SET(sockfd,&fdread);//将sockfd置为检测的状态(用来将一个给定的文件描述符加入集合之中)
struct timeval tv;
tv.tv_sec=5;//5秒间隔 select采集一次
tv.tv_usec=0;
char* result=(char*)malloc(sizeof(int));
memset(result,0,sizeof(int));
while(1){
int selection=select(sockfd+1,&fdread,NULL,NULL,&tv);//第一个参数为所有文件描述符的大值+1 用select去监测,这个io里面有无数据
if(!selection||!FD_ISSET(sockfd,&fdread)){//FD_ISSET判断描述符fd是否在给定的描述符集fdset中
break;
}else{
memset(buffer,0,BUFFER_SIZE);
int len=recv(sockfd,buffer,BUFFER_SIZE,0);//接受数据,放入buffer中
if(len==0){//disconnect
break;
}
result=(char*)realloc(result,(strlen(result)+len+1)*sizeof(char));//重新分配内存重建(+1是为了末尾'\0',不是最后一个的话会被覆盖)
strncat(result,buffer,len);//把buffer数据copy到resulut中
}
}
return result;
}
int main(int argc,char* argv[]){
if(argc<3) return -1;
char* response = http_send_request(argv[1],argv[2]);
printf("response:%s\n",response);
free(response);
}
结果请求的是百度的首页,所以只用了个斜杠
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流