\\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;
}