http://hi.baidu.com/qingshanyin/blog/item/c81db0fb31f5a0106d22ebe3.html
1、前言
伺服器端的構建,相比較構建客戶更加困難。但若只考慮網路部分時,兩種工作事實上是對等的。建立伺服器時遇到的主要困難主要來自像並發和資源處理這樣的問題。
(1)定義地址
(2)在該地址上開啟用於偵聽新串連的接受器,然後等待串連請求的到達
在等待過程中,要進行各種異常的處理,如錯誤編號是EINTR,表示網路出現問題,可繼續等待;ETIMEDOUT表示逾時,也可以繼續等待。無論如何,若有有異常,最好寫入一條日誌記錄,跟蹤事件。
(3)若開啟成功,將返回一個已經初始化的有效端對端對象,它代表與客戶的串連。此後的通訊中,伺服器和用戶端的區分已經不明顯了,因為那是一個雙向的對等通訊。
2、伺服器端代碼(與前面用戶端代碼一起,可以配合運行)
以下是伺服器端的代碼:
// BaseSocket_Practice_Server.cpp : 定義控制台應用程式的進入點。
//
#define ACE_NTRACE 0
#include "ace/INET_Addr.h"
#include "ace/SOCK_Stream.h"
#include "ace/SOCK_Acceptor.h"
#include "ace/Log_Msg.h"
#include "ace/Time_Value.h"
#include "ace/streams.h"//訊息流程檔案重新導向
#include "ace/Log_Record.h"//ACE_Log_Record對象定義
#include "ace/SString.h"
#include "define.h"
int ACE_TMAIN(int argc, ACE_TCHAR* argv[])
{
//ACE_OSTREAM_TYPE *output=new std::ofstream("server.test");
//ACE_LOG_MSG->msg_ostream(output);
//ACE_LOG_MSG->set_flags(ACE_Log_Msg::OSTREAM);//日誌重新導向到檔案
//ACE_LOG_MSG->clr_flags(ACE_Log_Msg::STDERR);//禁用標準裝置輸出
ACE_TRACE(ACE_TEXT("server main"));
//伺服器位址和連接埠
ACE_INET_Addr port_to_listen(5000,ACE_LOCALHOST);
//接聽程式
ACE_SOCK_Acceptor acceptor;
//開啟接聽程式,設定連接埠可複用
if(acceptor.open(port_to_listen,1)==-1)
{
ACE_ERROR_RETURN((LM_ERROR,
ACE_TEXT("%p/n"),
ACE_TEXT("acceptor.open")),
100);
}
//可以接受多個用戶端串連,但每次只能處理一個
while(1)
{
ACE_SOCK_Stream peer;
ACE_INET_Addr peer_addr;
ACE_Time_Value timeout(10,0);//用秒和微秒構造的時間:10秒,0微秒
//阻塞方式接受用戶端串連
//由於最後一個參數設定為0,因此當返回錯誤是EINTR的時候,即串連中斷時,迴圈退出
//但逾時情形則不退出迴圈
if(acceptor.accept(peer,&peer_addr,&timeout,0)==-1)
{
if(ACE_OS::last_error()==EINTR)//串連中斷
{
ACE_DEBUG((LM_DEBUG,
ACE_TEXT("(%p|%t) Interrupted while/n"),
ACE_TEXT("waitng for connection/n")));
}
else if(ACE_OS::last_error()==ETIMEDOUT)//等待逾時,若逾時寫一條日誌記錄,但監聽迴圈不退出
{
ACE_DEBUG((LM_DEBUG,
ACE_TEXT("(%p|%t) TimeOut while/n"),
ACE_TEXT("waiting for connection/n")));
}
}
else
{
ACE_TCHAR peer_name[MAXHOSTNAMELEN];
//擷取用戶端的IP地址和連接埠
peer_addr.addr_to_string(peer_name,MAXHOSTNAMELEN);
ACE_DEBUG((LM_DEBUG,ACE_TEXT("%s/n"),peer_name));
iovec buffer;
ssize_t bytes_received;
//將接收到的資料原封不動地給對端發送過去
while((bytes_received=peer.recvv(&buffer))!=-1)
{
if(bytes_received==0)
break;
ACE_DEBUG((LM_DEBUG,ACE_TEXT("%s%d/n"),ACE_reinterpret_cast(char*,buffer.iov_base),bytes_received));
peer.send_n(ACE_reinterpret_cast(char*,buffer.iov_base),bytes_received);
}
//每個用戶端通訊完畢,關閉流
peer.close();
ACE_DEBUG((LM_DEBUG,ACE_TEXT("Peer closed./n")));
}
}
//ACE_LOG_MSG->clr_flags(ACE_Log_Msg::OSTREAM);//非常重要,否則最後一條日誌將嘗試在已經刪除的output對象上寫
//delete output;//釋放記憶體
return (0);
}
3、小結
(1)若只考慮網路的話,伺服器端的建立與用戶端的建立工作對等;
(2)本程式和前面文章中的用戶端程式同時運行,示範用戶端和伺服器端的通訊,伺服器端將用戶端發來的資訊原封不動地回傳給用戶端。伺服器端可以處理多個客戶的串連,但每次只能服務一個客戶;
(3)從日誌資訊定向來看,兩個程式若在同一個電腦中啟動並執行話,顯然不能都寫往同一個目標流。本例中,伺服器端寫往控制台,用戶端則重新導向到檔案流;
(4)沒有解決的問題是,重新導向到檔案流的日誌,有亂碼出現(控制台顯示則沒有,調試跟蹤發現各變數也沒有亂碼字元)。這個問題沒有解決。