注册结果放在retData中,为了发给客户端,我们将结果中的特殊字符如双引号转码,如返回结果是:
- {"code":0, "msg":"ok"}
会被转码成:
- {%22code%22:0,%20%22msg%22:%22ok%22}
然后,将数据组装成http协议发给客户端,给客户端的应答协议与http请求协议有一点点差别,就是将请求的url路径换成所谓的http响应码,如200表示应答正常返回、404页面不存在。应答协议格式如下:
- GET或POST 响应码 HTTP协议版本号rn
- 2字段1名: 字段1值rn
- 3字段2名: 字段2值rn
- 4 …
- 5字段n名 : 字段n值rn
- 6rn
- 7http协议包体内容
举个例子如:
- HTTP/1.1 200 OKrn
- Content-Type: text/htmlrn
- Content-Length:42rn
- rn
- {%22code%22:%200,%20%22msg%22:%20%22ok%22}
注意,包头中的Content-Length长度必须正好是包体{%22code%22:%200,%20%22msg%22:%20%22ok%22}的长度,这里是42。这也符合我们浏览器的返回结果:

当然,需要注意的是,我们一般说http连接一般是短连接,这里我们也实现了这个功能(看上面的代码:conn->forceClose();),不管一个http请求是否成功,服务器处理后立马就关闭连接。
当然,这里还有一些没处理好的地方,如果你仔细观察上面的代码就会发现这个问题,就是不满足一个http包头时的处理,如果某个客户端(不是使用浏览器)通过程序模拟了一个连接请求,但是迟迟不发含有rnrn的数据,这路连接将会一直占用。我们可以判断收到的数据长度,防止别有用心的客户端给我们的服务器乱发数据。我们假定,我们能处理的最大url长度是2048,如果用户发送的数据累积不含rnrn,且超过2048个,我们认为连接非法,将连接断开。代码修改成如下形式:
- void HttpSession::OnRead(const std::shared_ptr<TcpConnection>& conn, Buffer* pBuffer, Timestamp receivTime)
- {
- //LOG_INFO << "Recv a http request from " << conn->peerAddress().toIpPort();
-
- string inbuf;
- //先把所有数据都取出来
- inbuf.append(pBuffer->peek(), pBuffer->readableBytes());
- //因为一个http包头的数据至少rnrn,所以大于4个字符
- //小于等于4个字符,说明数据未收完,退出,等待网络底层接着收取
- if (inbuf.length() <= 4)
- return;
-
- //我们收到的GET请求数据包一般格式如下:
- /*
- GET /register.do?p={%22username%22:%20%2213917043329%22,%20%22nickname%22:%20%22balloon%22,%20%22password%22:%20%22123%22} HTTP/1.1rn
- Host: 120.55.94.78:12345rn
- Connection: keep-alivern
- Upgrade-Insecure-Requests: 1rn
- User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36rn
- Accept-Encoding: gzip, deflatern
- Accept-Language: zh-CN, zh; q=0.9, en; q=0.8rn
- rn
- */
- //检查是否以rnrn结束,如果不是说明包头不完整,退出
- string end = inbuf.substr(inbuf.length() - 4);
- if (end != "rnrn")
- return;
- //超过2048个字符,且不含rnrn,我们认为是非法请求
- else if (inbuf.length() >= MAX_URL_LENGTH)
- {
- conn->forceClose();
- return;
- }
-
- //以rn分割每一行
- std::vector<string> lines;
- StringUtil::Split(inbuf, lines, "rn");
- if (lines.size() < 1 || lines[0].empty())
- {
- conn->forceClose();
- return;
- }
-
- std::vector<string> chunk;
- StringUtil::Split(lines[0], chunk, " ");
- //chunk中至少有三个字符串:GET+url+HTTP版本号
- if (chunk.size() < 3)
- {
- conn->forceClose();
- return;
- }
-
- LOG_INFO << "url: " << chunk[1] << " from " << conn->peerAddress().toIpPort();
- //inbuf = /register.do?p={%22username%22:%20%2213917043329%22,%20%22nickname%22:%20%22balloon%22,%20%22password%22:%20%22123%22}
- std::vector<string> part;
- //通过?分割成前后两端,前面是url,后面是参数
- StringUtil::Split(chunk[1], part, "?");
- //chunk中至少有三个字符串:GET+url+HTTP版本号
- if (part.size() < 2)
- {
- conn->forceClose();
- return;
- }
-
- string url = part[0];
- string param = part[1].substr(2);
-
- if (!Process(conn, url, param))
- {
- LOG_ERROR << "handle http request error, from:" << conn->peerAddress().toIpPort() << ", request: " << pBuffer->retrieveAllAsString();
- }
-
- //短连接,处理完关闭连接
- conn->forceClose();
- }
(编辑:晋中站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|