最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux環(huán)境下實(shí)現(xiàn)多進(jìn)程Socket通信功能

 更新時(shí)間:2025年05月30日 08:31:23   作者:天天進(jìn)步2015  
在網(wǎng)絡(luò)編程中,服務(wù)器與多個(gè)客戶端之間的通信是一個(gè)常見的需求,本文將介紹如何在Linux環(huán)境下實(shí)現(xiàn)一個(gè)服務(wù)器程序,該程序能夠啟動(dòng)多個(gè)客戶端進(jìn)程,并通過Socket與這些客戶端進(jìn)行通信,需要的朋友可以參考下

需求分析

我們的目標(biāo)是實(shí)現(xiàn)以下功能:

  1. 服務(wù)器能夠啟動(dòng)多個(gè)客戶端進(jìn)程,客戶端數(shù)量可配置
  2. 每個(gè)客戶端在不同的端口上監(jiān)聽
  3. 服務(wù)器能夠連接到所有客戶端
  4. 服務(wù)器能夠同時(shí)向所有客戶端發(fā)送消息
  5. 客戶端接收到消息后發(fā)送確認(rèn)
  6. 程序能夠優(yōu)雅地處理終止信號(hào)

系統(tǒng)架構(gòu)

整個(gè)系統(tǒng)由兩個(gè)主要組件組成:

  1. 客戶端程序(Client)

    • 在指定端口上監(jiān)聽連接
    • 接收服務(wù)器發(fā)送的消息
    • 發(fā)送確認(rèn)消息給服務(wù)器
  2. 服務(wù)器程序(Server)

    • 從配置文件讀取設(shè)置
    • 啟動(dòng)多個(gè)客戶端進(jìn)程
    • 連接到每個(gè)客戶端
    • 向所有客戶端發(fā)送消息
    • 接收客戶端的確認(rèn)消息

實(shí)現(xiàn)細(xì)節(jié)

客戶端實(shí)現(xiàn)(client.cpp)

客戶端程序需要?jiǎng)?chuàng)建一個(gè)Socket,在指定端口上監(jiān)聽連接,接收服務(wù)器發(fā)送的消息,并發(fā)送確認(rèn)。

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

#define BUFFER_SIZE 1024
#define DEFAULT_PORT 8888

bool running = true;

void signalHandler(int signum) {
    std::cout << "Interrupt signal (" << signum << ") received.\n";
    running = false;
}

int main(int argc, char* argv[]) {
    // 注冊(cè)信號(hào)處理函數(shù),用于優(yōu)雅關(guān)閉
    signal(SIGINT, signalHandler);
    signal(SIGTERM, signalHandler);
    
    // 解析命令行參數(shù)
    int port = DEFAULT_PORT;
    if (argc > 1) {
        port = std::stoi(argv[1]);
    }
    
    // 客戶端ID(進(jìn)程ID)
    pid_t pid = getpid();
    std::cout << "Client started with PID: " << pid << std::endl;
    
    // 創(chuàng)建Socket
    int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket < 0) {
        std::cerr << "Error creating socket" << std::endl;
        return 1;
    }
    
    // 設(shè)置服務(wù)器地址
    struct sockaddr_in serverAddr;
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(port);
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    
    // 綁定Socket到地址
    if (bind(clientSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
        std::cerr << "Error binding socket to port " << port << std::endl;
        close(clientSocket);
        return 1;
    }
    
    // 監(jiān)聽連接
    if (listen(clientSocket, 5) < 0) {
        std::cerr << "Error listening on socket" << std::endl;
        close(clientSocket);
        return 1;
    }
    
    std::cout << "Client listening on port " << port << std::endl;
    
    // 接受服務(wù)器連接
    struct sockaddr_in serverConnAddr;
    socklen_t serverLen = sizeof(serverConnAddr);
    int serverConnection = accept(clientSocket, (struct sockaddr*)&serverConnAddr, &serverLen);
    
    if (serverConnection < 0) {
        std::cerr << "Error accepting connection" << std::endl;
        close(clientSocket);
        return 1;
    }
    
    std::cout << "Connected to server at " 
              << inet_ntoa(serverConnAddr.sin_addr) 
              << ":" << ntohs(serverConnAddr.sin_port) 
              << std::endl;
    
    // 接收服務(wù)器消息
    char buffer[BUFFER_SIZE];
    while (running) {
        memset(buffer, 0, BUFFER_SIZE);
        int bytesReceived = recv(serverConnection, buffer, BUFFER_SIZE - 1, 0);
        
        if (bytesReceived > 0) {
            std::cout << "Message received: " << buffer << std::endl;
            
            // 發(fā)送確認(rèn)
            std::string ack = "Client " + std::to_string(pid) + " received message";
            send(serverConnection, ack.c_str(), ack.length(), 0);
        } 
        else if (bytesReceived == 0) {
            std::cout << "Server disconnected" << std::endl;
            break;
        }
        else {
            if (errno != EINTR) { // 忽略信號(hào)中斷
                std::cerr << "Error receiving data: " << strerror(errno) << std::endl;
                break;
            }
        }
    }
    
    // 清理資源
    close(serverConnection);
    close(clientSocket);
    std::cout << "Client terminated" << std::endl;
    
    return 0;
}

服務(wù)器實(shí)現(xiàn)(server.cpp)

服務(wù)器程序需要啟動(dòng)多個(gè)客戶端進(jìn)程,連接到每個(gè)客戶端,并向所有客戶端發(fā)送消息。

#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <thread>
#include <chrono>
#include <fstream>
#include <sstream>
#include <signal.h>
#include <sys/wait.h>
#include <mutex>
#include <condition_variable>

#define BUFFER_SIZE 1024
#define DEFAULT_BASE_PORT 8888
#define DEFAULT_NUM_CLIENTS 3
#define CONFIG_FILE "server_config.txt"

std::mutex mtx;
std::condition_variable cv;
bool allClientsConnected = false;
int connectedClients = 0;
int totalClients = 0;
std::vector<pid_t> clientPids;
std::vector<int> clientSockets;

// 從配置文件讀取配置
bool readConfig(int& numClients, int& basePort) {
    std::ifstream configFile(CONFIG_FILE);
    if (!configFile.is_open()) {
        std::cout << "Config file not found, using defaults" << std::endl;
        return false;
    }
    
    std::string line;
    while (std::getline(configFile, line)) {
        std::istringstream iss(line);
        std::string key;
        if (std::getline(iss, key, '=')) {
            std::string value;
            if (std::getline(iss, value)) {
                if (key == "NUM_CLIENTS") {
                    numClients = std::stoi(value);
                } else if (key == "BASE_PORT") {
                    basePort = std::stoi(value);
                }
            }
        }
    }
    
    configFile.close();
    return true;
}

// 寫入默認(rèn)配置到文件
void writeDefaultConfig() {
    std::ofstream configFile(CONFIG_FILE);
    if (configFile.is_open()) {
        configFile << "NUM_CLIENTS=" << DEFAULT_NUM_CLIENTS << std::endl;
        configFile << "BASE_PORT=" << DEFAULT_BASE_PORT << std::endl;
        configFile.close();
        std::cout << "Created default configuration file" << std::endl;
    } else {
        std::cerr << "Unable to create configuration file" << std::endl;
    }
}

// 啟動(dòng)客戶端進(jìn)程
pid_t launchClient(int port) {
    pid_t pid = fork();
    
    if (pid == 0) {
        // 子進(jìn)程
        std::string portStr = std::to_string(port);
        execl("./Client", "Client", portStr.c_str(), nullptr);
        
        // 如果execl返回,說明出錯(cuò)了
        std::cerr << "Error launching client: " << strerror(errno) << std::endl;
        exit(1);
    } else if (pid < 0) {
        // fork失敗
        std::cerr << "Fork failed: " << strerror(errno) << std::endl;
        return -1;
    }
    
    // 父進(jìn)程
    return pid;
}

// 連接到客戶端
bool connectToClient(int& clientSocket, int port) {
    clientSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket < 0) {
        std::cerr << "Error creating socket for client on port " << port << std::endl;
        return false;
    }
    
    struct sockaddr_in clientAddr;
    memset(&clientAddr, 0, sizeof(clientAddr));
    clientAddr.sin_family = AF_INET;
    clientAddr.sin_port = htons(port);
    clientAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    // 嘗試連接,帶重試
    int retries = 10;
    while (retries > 0) {
        if (connect(clientSocket, (struct sockaddr*)&clientAddr, sizeof(clientAddr)) == 0) {
            std::cout << "Connected to client on port " << port << std::endl;
            return true;
        }
        
        std::cout << "Connection attempt failed, retrying in 1 second..." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
        retries--;
    }
    
    std::cerr << "Failed to connect to client on port " << port << std::endl;
    close(clientSocket);
    return false;
}

// 連接客戶端的線程函數(shù)
void clientConnectionThread(int port, int clientIndex) {
    int clientSocket;
    if (connectToClient(clientSocket, port)) {
        std::lock_guard<std::mutex> lock(mtx);
        clientSockets[clientIndex] = clientSocket;
        connectedClients++;
        
        if (connectedClients == totalClients) {
            allClientsConnected = true;
            cv.notify_one();
        }
    }
}

// 向所有已連接的客戶端發(fā)送消息
void sendToAllClients(const std::string& message) {
    for (int socket : clientSockets) {
        if (socket > 0) {
            send(socket, message.c_str(), message.length(), 0);
            
            // 接收確認(rèn)
            char buffer[BUFFER_SIZE];
            memset(buffer, 0, BUFFER_SIZE);
            int bytesReceived = recv(socket, buffer, BUFFER_SIZE - 1, 0);
            if (bytesReceived > 0) {
                std::cout << "Acknowledgment: " << buffer << std::endl;
            }
        }
    }
}

// 清理資源
void cleanup() {
    // 關(guān)閉所有客戶端Socket
    for (int socket : clientSockets) {
        if (socket > 0) {
            close(socket);
        }
    }
    
    // 終止所有客戶端進(jìn)程
    for (pid_t pid : clientPids) {
        if (pid > 0) {
            kill(pid, SIGTERM);
            waitpid(pid, nullptr, 0);
        }
    }
}

// 信號(hào)處理函數(shù)
void signalHandler(int signum) {
    std::cout << "Interrupt signal (" << signum << ") received.\n";
    cleanup();
    exit(signum);
}

int main() {
    // 注冊(cè)信號(hào)處理函數(shù)
    signal(SIGINT, signalHandler);
    signal(SIGTERM, signalHandler);
    
    // 讀取配置
    int numClients = DEFAULT_NUM_CLIENTS;
    int basePort = DEFAULT_BASE_PORT;
    
    if (!readConfig(numClients, basePort)) {
        writeDefaultConfig();
    }
    
    std::cout << "Starting server with " << numClients << " clients, base port: " << basePort << std::endl;
    
    // 初始化客戶端向量
    totalClients = numClients;
    clientPids.resize(numClients, -1);
    clientSockets.resize(numClients, -1);
    
    // 啟動(dòng)客戶端進(jìn)程
    for (int i = 0; i < numClients; i++) {
        int port = basePort + i;
        pid_t pid = launchClient(port);
        
        if (pid > 0) {
            clientPids[i] = pid;
            std::cout << "Launched client " << i + 1 << " with PID " << pid << " on port " << port << std::endl;
        } else {
            std::cerr << "Failed to launch client " << i + 1 << std::endl;
        }
    }
    
    // 給客戶端啟動(dòng)的時(shí)間
    std::cout << "Waiting for clients to start..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));
    
    // 連接到客戶端
    std::vector<std::thread> connectionThreads;
    for (int i = 0; i < numClients; i++) {
        if (clientPids[i] > 0) {
            int port = basePort + i;
            connectionThreads.push_back(std::thread(clientConnectionThread, port, i));
        }
    }
    
    // 等待所有連接建立
    {
        std::unique_lock<std::mutex> lock(mtx);
        if (!allClientsConnected) {
            std::cout << "Waiting for all clients to connect..." << std::endl;
            cv.wait(lock, []{ return allClientsConnected; });
        }
    }
    
    // 等待所有連接線程結(jié)束
    for (auto& thread : connectionThreads) {
        thread.join();
    }
    
    std::cout << "All clients connected. Ready to send messages." << std::endl;
    
    // 主循環(huán)
    std::string message;
    while (true) {
        std::cout << "Enter message to send to all clients (or 'exit' to quit): ";
        std::getline(std::cin, message);
        
        if (message == "exit") {
            break;
        }
        
        std::cout << "Sending message to all clients..." << std::endl;
        sendToAllClients(message);
    }
    
    // 清理
    cleanup();
    std::cout << "Server terminated" << std::endl;
    
    return 0;
}

編譯和運(yùn)行

為了方便編譯,我們可以創(chuàng)建一個(gè)Makefile:

CC = g++
CFLAGS = -std=c++11 -Wall -pthread
LDFLAGS = -pthread

all: Client Server

Client: client.cpp
	$(CC) $(CFLAGS) -o Client client.cpp $(LDFLAGS)

Server: server.cpp
	$(CC) $(CFLAGS) -o Server server.cpp $(LDFLAGS)

clean:
	rm -f Client Server server_config.txt

.PHONY: all clean

編譯和運(yùn)行的步驟:

# 編譯
make

# 運(yùn)行服務(wù)器
./Server

代碼解析

端口號(hào)傳遞機(jī)制

服務(wù)器如何將端口號(hào)傳遞給客戶端是本系統(tǒng)的一個(gè)關(guān)鍵點(diǎn)。整個(gè)過程如下:

  1. 服務(wù)器為每個(gè)客戶端分配一個(gè)唯一的端口號(hào)(基礎(chǔ)端口號(hào) + 索引)
  2. 服務(wù)器通過fork()創(chuàng)建子進(jìn)程
  3. 子進(jìn)程通過execl()執(zhí)行客戶端程序,將端口號(hào)作為命令行參數(shù)傳遞
  4. 客戶端程序解析命令行參數(shù)獲取端口號(hào)
  5. 客戶端使用該端口號(hào)創(chuàng)建和綁定Socket
  6. 服務(wù)器知道每個(gè)客戶端的端口號(hào),并使用這些端口號(hào)連接到客戶端

信號(hào)處理

程序使用信號(hào)處理機(jī)制來實(shí)現(xiàn)優(yōu)雅關(guān)閉:

signal(SIGINT, signalHandler);
signal(SIGTERM, signalHandler);

這兩行代碼注冊(cè)了信號(hào)處理函數(shù),當(dāng)程序接收到SIGINT(通常是按Ctrl+C)或SIGTERM(通常是系統(tǒng)發(fā)送的終止信號(hào))時(shí),會(huì)調(diào)用signalHandler函數(shù)。在這個(gè)函數(shù)中,程序會(huì)清理資源并正常退出。

多線程連接

服務(wù)器使用多線程來并行連接到所有客戶端:

std::vector<std::thread> connectionThreads;
for (int i = 0; i < numClients; i++) {
    if (clientPids[i] > 0) {
        int port = basePort + i;
        connectionThreads.push_back(std::thread(clientConnectionThread, port, i));
    }
}

這樣可以同時(shí)嘗試連接到所有客戶端,而不是一個(gè)接一個(gè)地連接,提高了效率。

條件變量同步

服務(wù)器使用條件變量來等待所有客戶端連接完成:

{
    std::unique_lock<std::mutex> lock(mtx);
    if (!allClientsConnected) {
        std::cout << "Waiting for all clients to connect..." << std::endl;
        cv.wait(lock, []{ return allClientsConnected; });
    }
}

當(dāng)所有客戶端都連接成功后,allClientsConnected變量會(huì)被設(shè)置為true,條件變量會(huì)通知主線程繼續(xù)執(zhí)行。

總結(jié)

本文介紹了如何在Linux環(huán)境下實(shí)現(xiàn)一個(gè)服務(wù)器程序,該程序能夠啟動(dòng)多個(gè)客戶端進(jìn)程,并通過Socket與這些客戶端進(jìn)行通信。主要特點(diǎn)包括:

  1. 可配置的客戶端數(shù)量
  2. 動(dòng)態(tài)端口分配
  3. 并行連接
  4. 廣播消息
  5. 確認(rèn)機(jī)制
  6. 優(yōu)雅關(guān)閉

這個(gè)示例展示了多進(jìn)程、Socket通信、多線程和同步機(jī)制的綜合應(yīng)用,可以作為網(wǎng)絡(luò)編程的參考實(shí)現(xiàn)。

進(jìn)一步改進(jìn)

這個(gè)示例還可以進(jìn)一步改進(jìn),例如:

  1. 添加錯(cuò)誤恢復(fù)機(jī)制
  2. 實(shí)現(xiàn)客戶端自動(dòng)重連
  3. 添加消息隊(duì)列
  4. 實(shí)現(xiàn)更復(fù)雜的通信協(xié)議
  5. 添加安全機(jī)制(如TLS加密)

以上就是Linux環(huán)境下實(shí)現(xiàn)多進(jìn)程Socket通信功能的詳細(xì)內(nèi)容,更多關(guān)于Linux多進(jìn)程Socket通信的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Linux中screen命令及使用方法

    Linux中screen命令及使用方法

    Screen是一款由GNU計(jì)劃開發(fā)的用于命令行終端切換的自由軟件。這篇文章主要介紹了Linux中的screen命令及使用方法,需要的朋友可以參考下
    2020-02-02
  • Linux服務(wù)器出現(xiàn)崩潰或異常的故障排查和急救指南

    Linux服務(wù)器出現(xiàn)崩潰或異常的故障排查和急救指南

    當(dāng) Linux 服務(wù)器出現(xiàn)崩潰或異常時(shí),及時(shí)有效的故障排查是至關(guān)重要的,以下是一個(gè)綜合性的急救指南,幫助你在遇到問題時(shí)進(jìn)行快速的故障排查和修復(fù),需要的朋友可以參考下
    2025-11-11
  • Ubuntu系統(tǒng)Python崩潰的終極復(fù)活指南

    Ubuntu系統(tǒng)Python崩潰的終極復(fù)活指南

    你是否也經(jīng)歷過python3 --version 像被施了靜音咒,毫無反應(yīng);apt install 報(bào)錯(cuò)如雪崩,全是 py3compile: import not found;系統(tǒng)16個(gè)包卡死,連重裝 python3.8 都失敗,別慌,本文將帶你一步步手動(dòng)拆彈、強(qiáng)制修復(fù)、重建系統(tǒng)Python核心,需要的朋友可以參考下
    2025-10-10
  • Linux CentOS 6.5 卸載、tar安裝MySQL的教程

    Linux CentOS 6.5 卸載、tar安裝MySQL的教程

    這篇文章主要介紹了Linux CentOS 6.5 卸載、tar安裝MySQL的教程,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2019-11-11
  • CentOS7防火墻和端口相關(guān)命令介紹

    CentOS7防火墻和端口相關(guān)命令介紹

    大家好,本篇文章主要講的是CentOS7防火墻和端口相關(guān)命令介紹,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • linux 搭建svn服務(wù)器的方法步驟

    linux 搭建svn服務(wù)器的方法步驟

    這篇文章主要介紹了linux 搭建svn服務(wù)器的方法步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-01-01
  • Ubuntu14.04虛擬機(jī)安裝(圖文詳解)

    Ubuntu14.04虛擬機(jī)安裝(圖文詳解)

    本篇文章主要介紹了Ubuntu14.04虛擬機(jī)安裝,每一步都有詳細(xì)的圖文解釋,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-01-01
  • linux提示未找到命令unzip和zip的解決方案

    linux提示未找到命令unzip和zip的解決方案

    這篇文章主要介紹了linux提示未找到命令unzip和zip的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • JVM之jstack命令的使用解讀

    JVM之jstack命令的使用解讀

    jstack是Java線程分析工具,用于生成線程快照,排查死鎖和CPU過高問題,通過參數(shù)如-F、-l獲取線程狀態(tài)及鎖信息,解析Monitor機(jī)制與線程阻塞原因(如網(wǎng)絡(luò)瓶頸、sleep狀態(tài)),幫助定位性能問題
    2025-07-07
  • Lamp環(huán)境下設(shè)置綁定apache域名的方法分析

    Lamp環(huán)境下設(shè)置綁定apache域名的方法分析

    這篇文章主要介紹了Lamp環(huán)境下設(shè)置綁定apache域名的方法,簡(jiǎn)單分析了Lamp環(huán)境下的Apache域名綁定設(shè)置相關(guān)屬性、功能與操作技巧,需要的朋友可以參考下
    2018-03-03

最新評(píng)論

安图县| 石阡县| 南宫市| 土默特右旗| 江安县| 河源市| 万载县| 新民市| 思茅市| 道孚县| 汝阳县| 濮阳县| 台山市| 凌云县| 浠水县| 申扎县| 长岭县| 横山县| 天峻县| 红河县| 闻喜县| 新蔡县| 星子县| 蕉岭县| 高平市| 神池县| 天台县| 江达县| 阜康市| 锡林郭勒盟| 云南省| 广水市| 金川县| 九寨沟县| 双城市| 砀山县| 永丰县| 新竹市| 阿克| 乃东县| 陕西省|