首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++/CLI 程式進程間通訊:Socket 實作完整指南

     編輯:關於C++

前言

大型軟體專案常拆解為多個互動的小程式,彼此透過通訊協定交換資料。這些程式可能跨機器、跨作業系統、跨語言,也可能僅在同一台機器上運行,類似同一程式中的不同執行緒。本文專注於 C++/CLI 進程間通訊 (IPC),以 Socket 為核心機制,不涉及網路層面的遠端通訊。

架構概念:客戶端/伺服端模型

典型應用包含「伺服端」等待請求、「客戶端」發送請求。伺服端處理後回傳結果。實際系統中,一個程式可能同時扮演伺服端與客戶端角色(如資料庫伺服端需存取遠端檔案伺服器)。本文以「服務提供者」與「服務消費者」的抽象關係為基礎進行說明。

伺服端 Socket 實作

範例程式監聽指定連接埠,接收客戶端傳送的兩個整數,計算總和並回傳。

using namespace System;
using namespace System::IO;
using namespace System::Net;
using namespace System::Net::Sockets;

int main(array<String^>^ argv) {
    if (argv->Length != 1) {
        Console::WriteLine("Usage: Server port");
        Environment::Exit(1);
    }
    int port = 0;
    try { port = Int32::Parse(argv[0]); }
    catch (FormatException^) {
        Console::WriteLine("Port number {0} is ill-formed", argv[0]);
        Environment::Exit(2);
    }
    if (port < IPEndPoint::MinPort || port > IPEndPoint::MaxPort) {
        Console::WriteLine("Port number must be in the range {0}-{1}",
                           IPEndPoint::MinPort, IPEndPoint::MaxPort);
        Environment::Exit(3);
    }

    // 取得本機 IP 並建立端點
    IPAddress^ ipAddress = Dns::GetHostEntry(Dns::GetHostName())->AddressList[0];
    IPEndPoint^ ipEndpoint = gcnew IPEndPoint(ipAddress, port);

    // 建立 TCP Socket 並開始監聽
    Socket^ listenerSocket = gcnew Socket(AddressFamily::InterNetwork,
                                          SocketType::Stream, ProtocolType::Tcp);
    listenerSocket->Bind(ipEndpoint);
    listenerSocket->Listen(1); // 佇列長度 1
    Console::WriteLine("Server listener blocking status is {0}", listenerSocket->Blocking);

    // 接受連線
    Socket^ serverSocket = listenerSocket->Accept();
    Console::WriteLine("New connection accepted");
    listenerSocket->Close(); // 單一客戶端模式,關閉監聽 Socket

    // 建立資料流與讀寫器
    NetworkStream^ netStream = gcnew NetworkStream(serverSocket);
    BinaryReader^ br = gcnew BinaryReader(netStream);
    BinaryWriter^ bw = gcnew BinaryWriter(netStream);

    try {
        while (true) {
            int value1 = br->ReadInt32();
            int value2 = br->ReadInt32();
            Console::Write("Received values {0,3} and {1,3}", value1, value2);
            int result = value1 + value2;
            bw->Write(result);
            Console::WriteLine(", sent result {0,3}", result);
        }
    }
    catch (EndOfStreamException^) { /* 客戶端關閉連線 */ }
    catch (IOException^ e) {
        Console::WriteLine("IOException {0}", e);
    }

    serverSocket->Shutdown(SocketShutdown::Both);
    serverSocket->Close();
    netStream->Close();
    Console::WriteLine("Shutting down server");
}

關鍵步驟解析

  • 連接埠範圍:0-65535,其中 0-1023 保留,可用範圍 1024-65535。
  • IPEndPoint:封裝 IP 位址與連接埠,代表服務端點。
  • Socket 阻塞模式:預設阻塞於 Accept() 等待連線。
  • 雙 Socket 架構:listenerSocket 負責監聽,serverSocket 負責通訊,支援多客戶端擴展。
  • NetworkStream + BinaryReader/Writer:提供型別安全的二進位資料流讀寫。
  • 異常處理:捕捉 EndOfStreamException (正常關閉) 與 IOException (異常中斷)。

客戶端 Socket 實作

客戶端連線後發送隨機整數對,同步等待結果,每次間隔 3 秒以便觀察。

// ... 參數解析略 ...
IPAddress^ ipAddress = Dns::GetHostEntry(Dns::GetHostName())->AddressList[0];
IPEndPoint^ ipEndpoint = gcnew IPEndPoint(ipAddress, port);
Socket^ clientSocket = gcnew Socket(AddressFamily::InterNetwork,
                                    SocketType::Stream, ProtocolType::Tcp);
clientSocket->Connect(ipEndpoint);

NetworkStream^ netStream = gcnew NetworkStream(clientSocket);
BinaryReader^ br = gcnew BinaryReader(netStream);
BinaryWriter^ bw = gcnew BinaryWriter(netStream);

Random^ random = gcnew Random;
for (int i = 1; i <= messageCount; ++i) {
    int value1 = static_cast<int>(random->NextDouble() * 100);
    int value2 = static_cast<int>(random->NextDouble() * 100);
    bw->Write(value1);
    bw->Write(value2);
    Console::Write("Sent values {0,3} and {1,3}", value1, value2);
    int result = br->ReadInt32();
    Console::WriteLine(", received result {0,3}", result);
    Thread::Sleep(3000);
}

clientSocket->Shutdown(SocketShutdown::Both);
clientSocket->Close();
netStream->Close();
Console::WriteLine("Shutting down client");

執行範例輸出

伺服端輸出

Server listener blocking status is True
New connection accepted
Received values 42 and 69, sent result 111
Received values 66 and 71, sent result 137
Received values  7 and 93, sent result 100
Received values 43 and 65, sent result 108
Received values 45 and  3, sent result  48
Shutting down server

客戶端輸出

Sent values 42 and 69, received result 111
Sent values 66 and 71, received result 137
Sent values  7 and 93, received result 100
Sent values 43 and 65, received result 108
Sent values 45 and  3, received result  48
Notified server we're shutting down
Shutting down client

序列化與進階主題

實務上常需傳遞自訂物件(如金融交易:存款、轉帳、提款),需引入序列化機制。配合適當的序列化/反序列化,伺服端可處理多型物件請求。

思考練習

  1. 若伺服端連線佇列已滿,新客戶端連線請求會發生什麼?
  2. 若客戶端 Socket 仍開啟時伺服端關閉,反之亦然,會發生什麼?
  3. 修改伺服端支援多執行緒,同時處理多個客戶端(參考文末多執行緒輸出範例)。

多執行緒伺服端輸出範例

Server 2600
Waiting for new connection request
New connection accepted
Started thread Thread-1
Waiting for new connection request
Executing thread Thread-1
Received values 56 and 35, sent result 91
New connection accepted
Started thread Thread-2
Waiting for new connection request
Executing thread Thread-2
Received values 69 and 66, sent result 135
Received values 48 and 20, sent result 68
Received values 84 and 45, sent result 129
Received values  6 and 97, sent result 103
Shutting down server thread Thread-2
Received values 76 and  9, sent result  85
Shutting down server thread Thread-1
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved