使用http协议的好处
1、http协议简单,成熟
2、短链接获取数据后释放
不过在c++中,如果时简单的get 、 post 交互,c++ 不像java,node,或者go那样,随手就写出来,不理解网络包或者没有经验,无非就只能引入包,引入一个包并不难,本来很轻量的程序为了一个两个简单的交互引入越来越多的包并不是好事情,下面我们徒手写一段,可以使用该代码简单地获取数据而不用引入其他包。
http协议 get
int getData(const char* host, unsigned short port, const char* path, const char* get_content)
{
//GET请求方式
std::stringstream stream;
if(get_content!=NULL && strlen(get_content) > 0)
//if (strlen(get_content) > 0)
stream << "GET " << path << "?" << get_content;
else
stream << "GET " << path;
stream << " HTTP/1.0\r\n";
stream << "Host: " << host << "\r\n";
stream << "User-Agent: cool duck /1.1.1\r\n";
stream << "Connection:close\r\n\r\n";
//string temp = stream.str();
//cout << "len is " << temp.size() << endl;
return Http(host, port, stream.str().c_str());
}
这段程序使用标准的get 来向服务器获取请求内容,下面show me the code,直接使用socket和缓存获取内容。
int Http(const char* host, unsigned short port, const char *request)
{
SOCKET sockfd;
struct sockaddr_in address;
struct hostent *server;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
address.sin_family = AF_INET;
address.sin_port = htons(port);
//getaddrinfo()
server = gethostbyname(host);
memcpy((char *)&address.sin_addr.s_addr, (char*)server->h_addr, server->h_length);
#ifdef _WIN32
int ret = 0;
//int timeout = 2000; //2s
//这样做在Linux环境下是不会产生效果的,须如下定义:
struct timeval timeout = { 2,0 };
//设置发送超时
setsockopt(sockfd,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout, sizeof(struct timeval));
//设置接收超时
setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout, sizeof(struct timeval));
// ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout));
// ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
#endif
if (-1 == connect(sockfd, (struct sockaddr *)&address, sizeof(address))) {
#ifdef _WIN32
closesocket(sockfd);
#endif
cout << "connection error!" << std::endl;
return -1;
}
#ifdef WIN32
int iRes = send(sockfd, request, (int)strlen(request), 0);
if (iRes == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(sockfd);
return -1;
}
#else
struct timeval timeout = { 2,0 };//2s
int ret = setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout));
int ret = setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
write(sockfd, request, strlen(request));
#endif
_data.clear();
#define BUF_SIZE 128
char buf[128+1];
int offset = 0;
int rc;
do {
int buflen = BUF_SIZE - offset;
if (buflen == 0)
{
printf("not enough mem,so continue\n");
offset = 0;
buflen = BUF_SIZE;
//break;
}
#ifdef WIN32
rc = recv(sockfd, buf + offset, buflen, 0);
//rc = recv(sockfd, buf + offset, buflen, MSG_WAITALL);
#else
rc = read(sockfd, buf + offset, 1024);
#endif
if (rc > 0) {
offset += rc;
buf[rc] = '\0';
_data += buf;
printf("Bytes received: %d\n", rc);
}
else if (rc == 0)
{
printf("Connection closed\n");
}
else
printf("recv failed: %d\n",rc);
} while (rc > 0);
#ifdef WIN32
closesocket(sockfd);
#else
close(sockfd);
#endif
cout << _data << endl;
}
要点
函数里面定义缓存大小是关键,因为并不知道数据量到底有多大,所以定义为128字节
#define BUF_SIZE 128,写一个nodejs server 来直接返回数据,nodejs代码附在下方
实际上,http协议返回的头部一般都不止128字节,我们使用的量肯定是小的,可以自行修改BUF_SIZE,变大,比如256,甚至更大,在嵌入式系统中,显然字节数目不用过大。
其他:
1、使用循环缓冲来接收字节是比较好的方式,为了方便,直接使用stl的string 来存储接收的值。
2、没有解析所有http头部,实际上结束后可以使用string的find函数来查找字节偏移,得到内容字节数后再获取实际内容
3、如果是用来接收文件,这一段代码要简略修改,首先接收缓冲值要变大,其次不能使用stl string 的相加,可以解析完头部后直接存储写到外部存储中。
结果
调用函数,封装程http_content
int main()
{
http_connect conn;
conn.getData("127.0.0.1", 8000, "/", NULL);
conn.getData("127.0.0.1", 8000, "/test", NULL);
getchar();
//std::cout << "Hello World!\n";
}
以下为执行结果
图上可以看出,128字节缓存是不够的,分多次获取了数据,第一次get获取数据的时候拿到128字节数据,第二次的时候拿到78字节,流程收到服务器的close信号,直接退出,string 中保存了所有数据,但是需要解析,才能拿到{ret:ok},这个数据
总结
至此已经可以拿到get的数据,虽然简单,但是有效,不用引入额外的库。将在第二篇中讲解解析http头部和获取内容,以及post数据,在往后可以直接解析简单的json数据,而不引入库。
nodejs 服务器
这个就简单了,为了测试而已
var express = require('express');
var app = express();
var http = require('http').Server(app);
var httpget = require('http');
var bodyParser = require('body-parser');
var session = require('express-session');
//在线
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json());
app.use(session({
secret: 'secret',
resave: true,// don't save session if unmodified
saveUninitialized: false,// don't create session until something stored
cookie: {
maxAge: 1000 * 60 * 10 //过期时间设置(单位毫秒)
}
}));
app.use(express.static(__dirname));
app.get("/",function(req,res){
res.send("{ret:ok}");
});
app.get("/test",function(req,res){
res.send("{ret:test}");
});
app.post("/test",function(req,res){
res.send("{ret:test}");
});
http.listen(8000, function () {
console.log('listening on *:8000');
});