一些基本的HTTP协议问题(编程和理论)



在你决定这是一篇tl:dr(太长,没有阅读)的帖子之前,试着至少读一些,因为这是一个被分解成很多小片段的问题。其中一些你可能会回答并帮助我。

请尽你所能帮助我。这些类型的问题在互联网上很常见,我想你会帮助我和我之后的更多人。

我目前正在研究HTTP服务和协议本身,以便发现它是否对我有用。
我有一些基本问题以及一些需要讨论的代码。

首先我想知道沟通是如何开始的?我发现客户端发送了一条请求资源的消息(这是正确的吗?)。然后会发生什么?我(作为服务器)必须回复什么
是否需要在每次响应后附加回车和换行?在某个地方,它甚至说需要两个(\r\n\r\n)
如何建立异步写入?(我希望这个问题可以理解)我的主要目标是实现客户端和服务器之间的连接,然后实现从服务器到客户端的连续数据流。客户端是否需要对收到的每条消息进行回复
我希望我把问题说清楚,因为我不是这些方面的专家(但我对此很感兴趣)。

还有我问题的编程部分
我已经在C++(服务器端)中用Qt编写了一个简单的程序,在Objective C(iOS)中编写了一台简单的客户端。客户端连接,我可以读取请求标头。它是这样的:

Data available, incoming:  "GET / HTTP/1.1
Host: localhost:9990
Connection: close
User-Agent: CFStream%20test/1.0 CFNetwork/609 Darwin/12.2.0

我应该手动回复此标题吗?如果是,怎么办?

客户端代码看起来是这样的(我知道它不是伪代码,但我认为它非常不言自明):

- (void)setupStream
{
NSURL *url = [NSURL URLWithString:@"http://localhost:9990"];
CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (CFURLRef)url, kCFHTTPVersion1_1);
stream = CFReadStreamCreateForHTTPRequest(NULL, message);
CFRelease(message);
if (!CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue))
{
NSLog(@"Some error.");
}
CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, proxySettings);
CFRelease(proxySettings);
if (!CFReadStreamOpen(stream))
{
CFRelease(stream);
NSLog(@"Error opening stream.");
}
CFStreamClientContext context = {0, self, NULL, NULL, NULL};
CFReadStreamSetClient(stream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred, readStreamCallback, &context);
CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
NSLog(@"Done");
}

这是设置流方法。stream变量是CFReadStreamRef类型的类变量。

回调如下:

static void readStreamCallback(CFReadStreamRef aStream, CFStreamEventType event, void *client)
{
ViewController *controller = (ViewController*)client;
[controller handleEvent:event forStream:aStream];
}

句柄事件如下:

- (void)handleEvent:(CFStreamEventType)event forStream:(CFReadStreamRef)aStream
{
if (aStream != stream)
{
return;
}
NSLog(@"Handle event callback");
switch (event)
{
case kCFStreamEventHasBytesAvailable:
NSLog(@"Work log");
UInt8 bytes[11];
CFIndex length;
length = CFReadStreamRead(stream, bytes, 11); //I know 11 bytes is hard coded, its in testing stage now. Feel free to suggest me how to do it better.
if (length == -1)
{
NSLog(@"Error, data length = -1");
return;
}
NSLog(@"Len: %li, data: %s", length, bytes);
break;
default:
NSLog(@"Other event");
break;
}
}

这实际上就是所有值得一提的客户端代码。QtServer部分(我只发布重要部分)是这样做的:(这是一个子类QTcpServer类)。首先,startServer();被称为:

bool Server::startServer()
{
if (!this->listen(QHostAddress::Any, 9990))
return false;
return true;
}

当有一个连接传入时,incomingConnection会以套接字描述符为参数触发:

void Server::incomingConnection(int handle)
{
qDebug("New client connected");
ServerClient *client = new ServerClient(handle, this); //The constructor takes in the socket descriptor needed to set up the socket and the parent (this)
client->setVectorLocation(clients.count()); //This is a int from a Qvector in which i append the clients, its not important for understanding right now.
connect(client, SIGNAL(clientDisconnected(int)), this, SLOT(clientDisconnected(int)), Qt::QueuedConnection); //When the client socket emits a disconnected signal the ServerClient class emits a client disconnected signal which the server uses to delete that client from the vector (thats why I use "setVectorLocation(int)") - not important right now
clients.push_back(client); //And then I append the client to the QVector - not important right now
}

ClientServer类构造函数只创建一个新的套接字并连接所需的方法:

ServerClient::ServerClient(int handle, QObject *parent) :
QObject(parent)
{
socket = new QTcpSocket(this); //Socket is a class variable
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
socket->setSocketDescriptor(handle);
}

Ready read只会给我写传入的数据(我想以后不会有太多用户):

void ServerClient::readyRead()
{
qDebug() << "Data available, incoming: " << socket->readAll();
}

最后写入数据:

void ServerClient::writeData(QByteArray *data)
{
data->append("rnrn"); //I have read this must be appended to all outgoing data from a HTTP server
socket->write(*data);
socket->flush();
qDebug() << "Written data to client: " << *data;
}

但是,此代码并不总是有效的。有时,当我写像"message"这样的消息时,客户端会收到所有的数据和一些不应该存在的东西(新行和wierd符号-NSLog会导致这种情况吗?)。有时,当我发送"Hellow"时,客户端只收到"Hel"和其他一些时髦的东西。

有什么问题?我应该多注意什么?任何对我有帮助的都将不胜感激。请不要粘贴一些包含几百页书的链接,我相信只要向我解释就可以解决这个问题。

非常感谢
1月。

你问了很多问题。。。这是完全合法的做法:)

我承认-它太长了,我没有阅读:(

但是。。。

1) 是的,HTTP协议确实需要一个"CRLF"("\r\n")。许多服务器和许多客户端都是"宽容的",但严格来说——是的,你需要它们。

参考:RFC 2616

2) 想要了解HTTP"内部"也是完全合理的——我为你鼓掌。

一个好方法是阅读RFC。

另一种是使用"telnet"客户端:http://blog.tonycode.com/tech-stuff/http-notes/making-http-requests-via-telnet

另一个是研究FF Firebug 中的请求和响应

3) 套接字编程是另一个问题——这解释了为什么有时你可能会读到"你好世界",而有时你可能只会得到"hel"。

强烈推荐:Beej的网络编程指南

4) 最后,我真的不可能用C++在Qt中编写一个服务器(除了作为一个玩具"科学实验",或者一些真正的离线要求)

我肯定会用C#(适用于Windows服务器)、Java(适用于其他一切)或我觉得熟悉的脚本语言(Perl、Ruby/RoR、Python和Lua都在脑海中浮现)编写服务器代码。

IMHO。。希望这能有所帮助!

您的问题相当于"HTTP是如何工作的",而完整的答案在于规范。

相关内容

  • 没有找到相关文章

最新更新