HTTP协议是在TCP协议的基础上进行工作的,因此在发送HTTP请求之前要先建立TCP连接
HTTP协议并不复杂,其实就是通信双方约定的数据请求和应答请求的一个格式而已。如下:
请求行、响应行等还有它们各自的格式,也不复杂,这里不再描述。主要聊几种发送请求的方式。
向网站发送HTTP请求的几种方式___简单举例
以前以为只有浏览器才能进行HTTP请求,后发现自己大错特错。实际上是:只要能构建出HTTP请求消息格式发出去就可以能完成HTTP请求。当然,这里并不去探究HTTP深层次的东西,只是简单的了解一次HTTP请求的过程。
第一种_浏览器完成HTTP请求
几乎每次我们使用浏览器访问网站都会产生HTTP请求,这里我们可以随便打开几个网站,查看HTTP请求的信息,为了更方便观察HTTP请求的过程,在打开网站之前,可以先把wireshark打开,进行抓包。
这里用绿色对HTTP包进行标记,很明显我们能看到在HTTP请求开始前,首先是三个TCP,这也证实了HTTP请求的发送必须是在建立TCP连接基础上。上面出现的三个TCP就是TCP的三次握手。
右击“追踪流”,可看到HTTP请求的详细信息,红色部分是HTTP的请求信息,蓝色部分是HTTP响应的信息。
第二种_Telnet客户端发送HTTP请求
telnet是用来建立远程连接的,我们打开cmd命令行就可以使用telnet客户端,如果你的电脑不行,说明没有打开telnet功能,去控制面板“添加和删除windows功能”就可以找见telnet。
建立连接:
telnet www.baidu.com 80
与百度服务器建立连接,使用80端口,绝大多数的服务器使用80端口提供网站服务。
回车后cmd命令行完全变成黑框框,此时按下:
Ctrl + ]
开始使用telnet客户端:
继续回车,开启回显,准备填写HTTP请求信息:
向服务器发出HTTP的GRT请求,请求资源是根目录下的默认文件,使用HTTP1.1版本。主机名:www.baidu,com
GET / HTTP/1.1
host:www.baidu.com
[回车空行]
[再回车空行]
这样,向百度的HTTP请求就发出去了,马上就能得到HTTP响应:
当前页面内容,文件类型,使用的服务器类型等等,会看到很多信息。
第三种_高级程序设计语言拼接HTTP请求
既然知道了HTTP请求的格式,我们就可以使用高级程序设计语言及它们提供的一些网络的类或函数的工具去构建一个HTTP请求信息,然后向服务器发送。
这里的例子是使用PHP语言进行一次 GET请求:
定义一个接口:
interface proto
{
//连接ur1
function conn($ur1);
//发送get查询
function get();
//发送post查询
function post();
//关闭连接
function close();
}
定义一个类,实现接口,并添加其他内容:
class Http implements Proto
{
const CRLF = "\r\n"; //指定换行符
//错误设置
protected $errorstr = '';
protected $errorno = -1;
//返回信息保存
protected $responsed = '';
protected $url = null;
protected $fh = null;
protected $line = array();
protected $header = array();
protected $body = array();
//构造函数 有一个参数
public function __construct($url)
{
$this->conn($url);
$this->setHeader( 'Host: '.$this->ur1['host']);
}
//此方法负责写请求行
protected function setLine($method)
{
$this->line[0] = $method.' '.$this->ur1['path'].' '.'HTTP/1.1';
}
//此方法负责写头信息
protected function setHeader($headerline)
{
//构建请求头
$this->header[] = $headerline; //在构造函数中将 “host:”给出,同时使用url解析为数组后的host(主机信息)内容
}
//此方法负责写主体信息
protected function setBody()
{
//如果是GET请求,则不需要此项
}
//连接ur1 构造函数中调用此函数,传入参数 $url
public function conn($ur1)
{
$this->ur1 = parse_url($ur1); //将 url 各个部分解析成数组 保存到数据成员url中
if(!isset($this->url['port'])) //检测url中是否指定了端口如果没有,则设置端口为 80
{
$this->url['port'] = 80;
}
//print_r($this->url);
$this->fh = fsockopen($this->ur1['host'] , $this->url['port'] , $this->errorno , $this->errorstr , 40);
}
//发送get查询
public function get()
{
$this->setLine('GET');
$this->request();
return $this->responsed;
}
public function reply()
{
print_r($this->responsed);
}
//发送post查询
public function post()
{
//如果是GET请求,则不需要此项
}
//真正的请求
public function request()
{
//拼接一个正式 http 请求信息
$req = array_merge($this->line,$this->header,array(''),$this->body,array(''));
//print_r($req);
$req = implode(self::CRLF, $req);
echo $req;
fwrite($this->fh, $req);
while (!feof($this->fh))
{
$this->responsed .=fread($this->fh, 1024);
}
$this->close();
}
//关闭连接
public function close()
{
}
}
然后将这个php文件放到一个服务器上,我们去访问一下
右击查看源码便是上面的结果。能明显的看到请求和响应的信息。不过,如果你想尝试需要搭建个能解析PHP语言的服务器,我这里用的是PHPstudy集成的Apache中间件服务器。
第四种_使用高级语言写好的框架发送HTTP
这里的例子用C++的Qt框架,这种方式相对而言更加简单,因为HTTP请求的大的框架已经是写好了,我们只需要去用就行,不必像上一种办法那样从相对底层的地方去构造、拼接HTTP请求信息。
一些基本显示框的布局:
replyBodyEdit = new QTextEdit;
replyBodyEdit->setFixedSize(250,300);
replyHeadEdit = new QTextEdit;
replyHeadEdit->setFixedSize(250,300);
QGridLayout *mainLayout = new QGridLayout(this);
mainLayout->addWidget(replyBodyEdit,0,1);
mainLayout->addWidget(replyHeadEdit,0,0);
处理请求完成后返回的响应信息的函数如下。
把这个函数作为槽函数,触发的它信号是QNetworkAccessManager类的一个:当响应信息完成时发出的finished信号。
void qHTTPWidget::finishedSlot(QNetworkReply *reply)
{
if(reply->error() == QNetworkReply::NoError)
{
//返回访问资源文件格式信息
QVariant varint1 = reply->header(QNetworkRequest::ContentTypeHeader);
//QString contentType = "Content-Type:" + varint1.toString();
replyHeadEdit->append(varint1.toString() );
QVariant varint2 = reply->header(QNetworkRequest::ContentLengthHeader);
replyHeadEdit->append(varint2.toString());
qDebug()<<varint2.toInt();
QVariant varint4 = reply->header(QNetworkRequest::LastModifiedHeader);
replyHeadEdit->append(varint4.toString());
QVariant varint6 = reply->header(QNetworkRequest::ServerHeader);
replyHeadEdit->append(varint6.toString());
QVariant varint7 = reply->header(QNetworkRequest::UserAgentHeader);
replyHeadEdit->append(varint7.toString());
QVariant varint3 = reply->header(QNetworkRequest::LocationHeader);
replyHeadEdit->append(varint3.toString());
QByteArray bytes = reply->readAll();
replyBodyEdit->setText(bytes);
}
else
{
qDebug()<<"错误";
replyHeadEdit->setText("错误");
qDebug("error:%d\n",(int)reply->error());
qDebug(qPrintable(reply->errorString()));
}
reply->deleteLater();
}
这是请求信息的设置:
QNetworkRequest request;
request.setUrl(QUrl("http://192.168.199.128")); //一定要写“http://”不能只写ip或者域名
这是上面提到的需要进行的信号和槽的连接:
QNetworkAccessManager *accessManager = new QNetworkAccessManager(this);
connect(accessManager,&QNetworkAccessManager::finished,this,&qHTTPWidget::finishedSlot);
效果如下,左边是响应信息的响应行和头,右侧是响应主体。这个请求的是我自己搭的一个IIS站点,写了一个简单的表单。
关于HTTP的内容和HTTP请求发送方式的东西远不止这些,起码我们知道每种语言都可以构建HTTP请求,也都不是很复杂,但这其中有很多细节性的东西是得亲手尝试和反复练习才能知晓的。关于HTTP我还有更多需要去学。