\\Server\Pipe\[Path]Name
其中,第一部分\\Server指定了服务器的名字,命名管道服务即在此服务器创建,其字串部分可表示为一个小数点(表示本机)、星号(当前网络字段)、域名或是一个真正的服务;第二部分\Pipe在使用时不能变;第三部分\[Path]Name则使应用程序可以唯一定义及标识一个命名管道的名字,可以设置多级目录。chromium使用的命名是
\\.\pipe\chrome.$channel_id
也就是在”\\.\pipe\chrome.“后接一个channel_id为管道命名。
命名管道在服务端需要调用CreateNamedPipe创建管道,然后调用ConnectNamedPipe接收来自客户端的连接。在客户端,使用CreateFile打开管道。管道服务端在调用CreateNamedPipe创建命名管道需要为指定管道名称。对于管道客户端,则是在调用CreateFileW函数连接命名管道时对管道名称进行指定。在管道双方都可以进行读写的操作,如WriteFile、ReadFile,或者调用CloseHandle断开连接。命名管道的使用过程很像socket,只是调用的方法不一样。
chromium中管道的创建和收发数据最终都由一个类Channel::ChannelImpl::ChannelImpl完成,为了进行管道的收发,定义了两个state,分别是input_state_和output_state_,其中input_state_在用于接收客户端连接或者接收数据时使用,而output_state_则在发送数据时使用,在以下函数中都可以看到这两个state的使用。
BOOL ok = ReadFile(pipe_, buffer, buffer_len,
&bytes_read, &input_state_.context.overlapped);
BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped);
BOOL ok = WriteFile(pipe_,
m->data(),
static_cast<int>(m->size()),
&bytes_written,
&output_state_.context.overlapped);
先来看看state的定义,它有两个重新的成员,is_pending表示一个接收数据、发送数据事件是否在进行中。input_state_在服端端进行等待接收客户端连接,或者服务端客户端接收数据的过程中,is_pending都会设置成true;output_state_在进行数据发送的过程中,is_pending会设置成true。在io事件处理完成后,is_pending会在OnIOCompleted函数中进行重置。在Channel::ChannelImpl::Send的处理逻辑中,可以发现,如果output_state_.is_pending设为true的话,消息会被放到一个output_queue_中,不会直接发送,保证管道同一时间只发送一个消息。
struct State {
explicit State(ChannelImpl* channel);
~State();
base::MessageLoopForIO::IOContext context;
bool is_pending;
};
Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) {
memset(&context.overlapped, 0, sizeof(context.overlapped));
context.handler = channel;
}
state另一个属性context,它的类型是IOContex,它的定义为
struct IOContext {
OVERLAPPED overlapped;
IOHandler* handler;
};
overlapped主要用于区别完成的io事件,在上文调用ReadFile、 ConnectNamedPipe、WriteFile的例子中都有使用。在io事件完成后,由GetQueuedCompletionStatus捕获。handler是指向Channel::ChannelImpl::ChannelImpl自已的指针,因为ChannelImpl继承了MessageLoopForIO::IOHandler接口,实现了OnIOCompleted函数。
void Channel::ChannelImpl::OnIOCompleted(
base::MessageLoopForIO::IOContext* context,
DWORD bytes_transfered,
DWORD error)
注意到,overlapped的地址其实就是IOContext的地址,在MessagePumpForIO中,它们的地址就是直接这样转换的
item->context = reinterpret_cast<IOContext*>(overlapped);
在OnIOCompleted中,通过context是等于input_state_.context还是output_state_.context判断完成的是输入事件还是输出事件,从而做出相应的处理。在对于接收处理,分为两种:
1、等待客户端连接完成,对于服务端waiting_connect_在初始是为true,首先调用ProcessConnection检查连接是否建立完成,正常的情况下ProcessConnection都会返回true,这时候OnIOCompleted返回。
2、异步接收数据完成,这时候调用AsyncReadComplete把接收完成的bytes_transfered个字节与之前接收的字节组合起来,看是否完成了一整条消息,如果完成了消息,则调用消息处理函数对消息进行处理。若管道没有异常,ok都会为真值,调用ProcessIncomingMessages函数,接着进行异步的数据读取。
void Channel::ChannelImpl::OnIOCompleted(
base::MessageLoopForIO::IOContext* context,
DWORD bytes_transfered,
DWORD error) {
bool ok = true;
if (context == &input_state_.context) {
if (waiting_connect_) {
if (!ProcessConnection())
return;
if (!output_queue_.empty() && !output_state_.is_pending)
ProcessOutgoingMessages(NULL, 0);
if (input_state_.is_pending)
return;
}
base::AutoReset<bool> auto_reset_processing_incoming(
&processing_incoming_, true);
if (input_state_.is_pending) {
input_state_.is_pending = false;
if (!bytes_transfered)
ok = false;
else if (pipe_ != INVALID_HANDLE_VALUE)
ok = AsyncReadComplete(bytes_transfered);
} else {
DCHECK(!bytes_transfered);
}
if (ok)
ok = ProcessIncomingMessages();
} else {
ok = ProcessOutgoingMessages(context, bytes_transfered);
}
if (!ok && INVALID_HANDLE_VALUE != pipe_) {
Close();
listener()->OnChannelError();
}
}
在处理发送完成事件时,如果output_state_.is_pending为真,表示上一条消息在等待发送,而这时这条消息的发送已经完成,把消息从队列中删除。然后查看队列中是否还有其它需要发送的消息,接着调用WriteFile发送消息,注意在异步IO中WriteFile返回的一定是0,但是ERROR_IO_PENDING并不表明发送失败,只是等待异步操作完成。
bool Channel::ChannelImpl::ProcessOutgoingMessages(
base::MessageLoopForIO::IOContext* context,
DWORD bytes_written) {
if (output_state_.is_pending) {
output_state_.is_pending = false;
if (!context || bytes_written == 0) {
DWORD err = GetLastError();
LOG(ERROR) << "pipe error: " << err;
return false;
}
Message* m = output_queue_.front();
output_queue_.pop();
delete m;
}
if (output_queue_.empty())
return true;
if (INVALID_HANDLE_VALUE == pipe_)
return false;
Message* m = output_queue_.front();
DCHECK(m->size() <= INT_MAX);
BOOL ok = WriteFile(pipe_,
m->data(),
static_cast<int>(m->size()),
&bytes_written,
&output_state_.context.overlapped);
if (!ok) {
DWORD err = GetLastError();
if (err == ERROR_IO_PENDING) {
output_state_.is_pending = true;
return true;
}
return false;
}
output_state_.is_pending = true;
return true;
}
管道连接的逻辑调用Connect函数完成。对于服务端waiting_connect_的初始值为true,那么进入函数ProcessConnection后调用ConnectNamedPipe,这时分两种情况:
1、在服务端调用CreateNamedPipe之后调用ConnectNamedPipe函数之前,有客户端调用了CreateFile,这时候管道已经连接完成,GetLastError()返回ERROR_PIPE_CONNECTED,waiting_connect_设为false,input_state_.is_pending为false,这个状态表示连接已经建立,但是没还有开始等待接收数据,随后调用PostTask,进入OnIOCompleted函数,调用ProcessIncomingMessages等待接收数据。
2、如果没有客户端进行连接,这时GetLastError()返回ERROR_IO_PENDING,这时把input_state_.is_pending设为true,不调用OnIOCompleted,因为此时连接没有建立完成,不需要等待接收数据。
对于客户端,waiting_connect_初始设置为false,在调用CreateFile就能判断管道是否已经正常建立,只要pipe_不为INVALID_HANDLE_VALUE,那么管道已经建立了。只需要调用OnIOCompleted等待接收数据。
bool Channel::ChannelImpl::Connect() {
if (!thread_check_.get())
thread_check_.reset(new base::ThreadChecker());
if (pipe_ == INVALID_HANDLE_VALUE)
return false;
base::MessageLoopForIO::current()->RegisterIOHandler(pipe_, this);
if (waiting_connect_)
ProcessConnection();
if (!input_state_.is_pending) {
base::MessageLoopForIO::current()->PostTask(
FROM_HERE,
base::Bind(&Channel::ChannelImpl::OnIOCompleted,
weak_factory_.GetWeakPtr(),
&input_state_.context,
0,
0));
}
if (!waiting_connect_)
ProcessOutgoingMessages(NULL, 0);
return true;
}
bool Channel::ChannelImpl::ProcessConnection() {
if (input_state_.is_pending)
input_state_.is_pending = false;
if (INVALID_HANDLE_VALUE == pipe_)
return false;
BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped);
DWORD err = GetLastError();
if (ok) {
NOTREACHED();
return false;
}
switch (err) {
case ERROR_IO_PENDING:
input_state_.is_pending = true;
break;
case ERROR_PIPE_CONNECTED:
waiting_connect_ = false;
break;
case ERROR_NO_DATA:
return false;
default:
NOTREACHED();
return false;
}
return true;
}