• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 94, comments - 250, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

            Nebula3學習筆記(6): 網絡系統

            Posted on 2008-12-14 21:32 Condor 閱讀(1198) 評論(0)  編輯 收藏 引用

            Nebula3的網絡子系統提供了基于TCP協議的簡單C/S通信模式. 它并沒有打算做成大廳,會話管理還有玩家數據同步的面向游戲的高級通信. 這些以后會在更高層的Nebula3子系統中出現.

            使用IP地址

              一個IpAddress對象通過主機名字或TCP/IP地址加一個端口號定義了一個通信端點. IpAddress對象可以通過多數方式建立:

            1: // 從 TCP/IP 地址和端口號:

            2: IpAddress ipAddr("192.168.0.2",1234);

            3:

            4: // 從主機名和端口號:

            5: IpAddress ipAddr("www.radonlabs.de",1234);

            6:

            7: // 從本機(127.0.0.1) 和端口號:

            8: IpAddress ipAddr("localhost",1234);

            9:

            10: // 從"any" 地址 (0.0.0.0) 和端口號:

            11: IpAddress ipAddr("any",1234);

            12:

            13: // 從廣播地址 (255.255.255.255) 和端口號:

            14: IpAddress ipAddr("broadcast",1234);

            15:

            16: // 從主機的第一個合法網絡適配器的地址和端口號

            17: IpAddress ipAddr("self",1234);

            18:

            19: // 從主機的第一個連接到互聯網的網絡適配器的地址和端口號:

            20: IpAddress ipAddr("insetself",1234);

            21:

            22: // 從一個定義了主機名的URI和端口號:

            23: IpAddress ipAddr(IO::URI("http://www.radonlabs.de:2100"));

              一個IpAddress對象可以用于從主機名查找TCP/IP地址:

            1: IpAddress ipAddr("www.radonlabs.de",0);

            2: String numericalAddr = ipAddr.GetHostAddr();

            建立一個客戶端/服務器系統

              網絡子系統用TcpServer和TcpClient類實現了一個易用的基于TCP協議的C/S系統. 一個TcpServer可以為任意數量的TcpClient服務.

              建立一個服務器可以這么做:

            1: using namespace Net;

            2:

            3: Ptr<TcpServer> tcpServer = TcpServer::Create();

            4: tcpServer->SetAddress(IpAddress("any",2352));

            5: if(tcpServer->Open())

            6: {

            7: // TcpServer successfully opened

            8: }

              這樣會建立一個在2352端口監聽客戶端連接請求的服務器.

              為了跟TcpServer通信, 需要在客戶端建立一個TcpClient對象:

            1: using namespace Net;

            2:

            3: Ptr<TcpClient> tcpClient = TcpClient::Create();

            4: tcpClient->SetBlocking(false);

            5: tcpClient->SetAddress(IpAddress("localhost",2352));

            6: TcpClient::Result res = tcpClient->Connect();

              這里假設服務端和客戶端運行在同一臺機器上(因為客戶端連接到了”localhost”).

              像上面那樣非阻塞的情況, Connect()方法不是返回TcpClient::Success(這意味著連接建立好了)就是TcpClient::Connecting, 如果這樣的話, 應用程序需要繼續調用Connect()方法. 如果連接錯誤, 會返回一個TcpClient::Error的返回值.

              如果是阻塞的, Connect()方法直到連接建立(結果是TcpClient::Success)或發生錯誤才會返回.

              注意:一個交互式應用程序不應該在網絡通信時阻塞, 而應不斷地為用戶提供反饋.

              一旦連接建立, 服務端會為每個客戶機建立一個TcpClientConnection對象. TcpClientConnection在服務器上表示客戶機, 并且負責從客戶機收發數據.

              要進行接收和發送數據的話, 需使用IO::Stream對象. 在通信流上連接IO::StreamReader和IO::StreamWriter對象后, 從流中編碼和解碼數據是一件非常容易的事情.

              注意:發送數據并不是即時的, 而是在Send()方法被調用之前會一直保存在發送流當中.

              要客戶端給服務器發送一些文本數據話, 只要從發送流獲取一個指針, 向其中寫入數據后調用Send()方法就可以了:

            1: using namespace Net;

            2: using namespace IO;

            3:

            4: // obtain pointer to client's send stream and attach a TextWriter

            5: const Ptr<Stream>& sendStream = tcpClient->GetSendStream();

            6: Ptr<TextWriter> textWriter = TextWriter::Create();

            7: textWriter->SetStream(sendStream);

            8: textWriter->Open())

            9: textWriter->WriteString("Hello Server");

            10: textWriter->Close();

            11:

            12: // send off the data to the server

            13: if(this->tcpClient->Send())

            14: {

            15: // data has been sent

            16: }

              在服務器端接收客戶端數據, 應用程序需要要頻繁地(每幀一次)緩存帶有客戶羰數據的TcpClientConnection. 可能不只一個TcpClientConnection在等待處理, 因此處理循環應該像這樣:

            1: // get array of client connections which received data since the last time

            2: Array<Ptr<TcpClientConnection>> recvConns = tcpServer->Recv();

            3: IndexT i;

            4: for(i =0; i < recvConns.Size(); i++)

            5: {

            6: // get receive stream from current connection, attach a text reader and read content

            7:      Ptr<TextReader> textReader = TextReader::Create();

            8:      textReader->SetStream(recvConns[i]->GetRecvStream());

            9:      textReader->Open();

            10:      String str = textReader->ReadString();

            11:      textReader->Close();

            12:

            13: // process received string and send response back to client

            14: // create a TextWriter and attach it to the send stream of the client connection

            15:      Ptr<TextWriter> textWriter = TextWriter::Create();

            16:      textWriter->SetStream(recvConns[i]->GetSendStream());

            17:      textWriter->Open();

            18:      textWriter->WriteString("Hello Client");

            19:      textWriter->Close();

            20:

            21: // finally send the response back to the client

            22:      recvConns[i]->Send();

            23: }

              在客戶端獲得服務器的應答, 調用TcpClient::Recv()方法會在數據到達之前一直阻塞(在阻塞模式下), 或者立即返回(在非阻塞模式下), 并在有服務器數據時返回true:

            1: // check if data is available from the server

            2: if(tcpClient->Recv())

            3: {

            4: // yep, data is available, get the recv stream and read the data from it

            5: const Ptr<Stream>& recvStream = tcpClient->GetRecvStream();

            6:      Ptr<TextReader> textReader = TextReader::Create();

            7:      textReader->SetStream(recvStream);

            8:      textReader->Open();

            9:      String responseString = textReader->ReadString();

            10:      n_printf("The server said: %s\n", responseString.AsCharPtr());

            11:      textReader->Close();

            12: }

              客戶端也應該通過調用IsConnected()訪求檢查連接是否有效. 如果因為某些原因使連接斷開, 這個方法會返回false.

              注意:

            TcpServer和TcpClient并沒有為能夠跟不相關的客戶端和服務器端而實現一個潛在的通信協議(例如, 一個TcpServer可以跟標準的Web瀏覽器客戶端一起工作, 還有一個TcpClient類可以跟一個標準的HTTP服務器通信).

              現實世界的情況是, 一個應用程序應該實現自己的健壯的通信協議, 它至少會編碼負載數據的長度. 如果負載比最大包大小還要大, 數據會以多個包發送并在客戶端接收. 客戶端應該把數據解碼成一個完整的消息, 否則需要等待消息的數據接收完畢.

            字節次序問題

              服務器和客戶端可能運行在不同字節次序的的CPU上. 如果二進制數據通過網絡發送, 數據必需轉換成兩個客戶端都一致的”網絡字節順序”. Nebula3在IO::BinaryReader和IO::BinaryWriter類中提供字節順序的自動轉換. 只需要簡單地調用下面的方法在網絡通信流上讀寫就可以了:

            1: binaryReader->SetStreamByteOrder(System::ByteOrder::Network);

            2: binaryWriter->SetStreamByteOrder(System::ByteOrder::Network);

            Socket

              網絡子系統提供了一個把傳統socket函數包裝成C++接口的Socket類. 一般情況下應用程序不直接使用Socket類, 而是使用更高級的像TcpServer這樣的類. 但也不是不可能在有的時候直接使用socket函數比Socket類更方便.

            国产美女亚洲精品久久久综合| 久久青草国产精品一区| 中文字幕无码久久人妻| 久久婷婷五月综合97色直播| 日产精品久久久一区二区| 久久精品一区二区三区不卡| 精品久久久久久99人妻| 久久人人爽人人爽人人爽| 久久99精品久久久久子伦| 久久久久噜噜噜亚洲熟女综合| 久久狠狠爱亚洲综合影院| 伊人丁香狠狠色综合久久| 久久天天躁夜夜躁狠狠| 国内精品欧美久久精品| 蜜臀久久99精品久久久久久小说| 国产精品丝袜久久久久久不卡| 中文字幕人妻色偷偷久久| 国产农村妇女毛片精品久久| 亚洲色大成网站www久久九| 久久精品国产亚洲7777| 国内精品久久久久伊人av| 亚洲国产成人精品女人久久久 | 色综合久久无码五十路人妻| 久久精品这里只有精99品| 国产精品久久99| 欧美噜噜久久久XXX| 久久无码AV一区二区三区| 久久精品成人免费国产片小草| 成人久久久观看免费毛片| 无码人妻精品一区二区三区久久| 亚洲午夜无码久久久久小说| 久久国产热这里只有精品| 一本久久久久久久| 国产精品无码久久四虎| 国产精品成人99久久久久| 99精品伊人久久久大香线蕉| 亚洲一区中文字幕久久| 91久久香蕉国产熟女线看| 91秦先生久久久久久久| 91亚洲国产成人久久精品网址| 99久久精品费精品国产|