Linux網路編程:socket & pthread_create()多線程 實現clients/server通信

来源:https://www.cnblogs.com/caojun97/archive/2023/05/19/17411101.html
-Advertisement-
Play Games

一、問題引入 Linux網路編程:socket & fork()多進程 實現clients/server通信 隨筆介紹了通過fork()多進程實現了伺服器與多客戶端通信。但除了多進程能實現之外,多線程也是一種實現方式。 重要的是,多進程和多線程是涉及操作系統層次。隨筆不僅要利用pthread_cre ...


一、問題引入

Linux網路編程:socket & fork()多進程 實現clients/server通信 隨筆介紹了通過fork()多進程實現了伺服器與多客戶端通信。但除了多進程能實現之外,多線程也是一種實現方式。

重要的是,多進程和多線程是涉及操作系統層次。隨筆不僅要利用pthread_create()實現多線程編程,也要理解線程和進程的區別。

二、解決過程

client 代碼無需修改,請參考 Linux網路編程:socket & fork()多進程 實現clients/server通信

2-1 server 代碼

#include <stdlib.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/syscall.h>

#define IP "10.8.198.227"
#define PORT 8887
#define gettid() syscall(__NR_gettid)
#define PTHREAD_MAX_SIZE 3    // 允許最大客戶端連接數

typedef struct PTHREAD_DATA_ST
{
    pthread_t pthread_id;
    int connfd;
    char socket[128];
    struct sockaddr_in cliaddr;
}PTHREAD_DATA_ST;

//static int g_pthread_num = 3; // 允許最大客戶端連接數
static struct PTHREAD_DATA_ST g_pthread_data[PTHREAD_MAX_SIZE];

static void pthread_data_index_init(void)
{
    for (int i = 0; i < PTHREAD_MAX_SIZE; i++)
    {
        memset(&g_pthread_data[i], 0 , sizeof(struct PTHREAD_DATA_ST));
        g_pthread_data[i].connfd = -1;
    }
}

static int pthread_data_index_find(void)
{
    int i;
    for (i = 0; i < PTHREAD_MAX_SIZE; i++)
    {
        if (g_pthread_data[i].connfd == -1)
            break;
    }
    return i;
}

static int string_toupper(const char *src, int str_len, char *dst)
{
    int count = 0;
    for (int i = 0; i < str_len; i++)
    {
        dst[i] = toupper(src[i]);
        count++;
    }
    return count;
}

void *pthread_handle(void *arg)
{
    struct PTHREAD_DATA_ST *pthread = (struct PTHREAD_DATA_ST *)arg;
    int connfd = pthread->connfd;
    int recv_len, send_len;
    pid_t tid = gettid();
    char read_buf[1024], write_buf[1024];
    while (1)
    {
        memset(read_buf, 0, sizeof(read_buf));
        memset(write_buf, 0, sizeof(write_buf));
        recv_len = read(connfd, read_buf, sizeof(read_buf));
        if (recv_len <= 0)
        {
            printf("%s close, child %d terminated\n", pthread->socket, tid);
            close(connfd);
            pthread->connfd = -1;
            pthread_exit(NULL);
        }
        printf("%s:%s(%d Byte)\n", pthread->socket, read_buf, recv_len);
        send_len = string_toupper(read_buf, strlen(read_buf), write_buf);
        write(connfd, write_buf, send_len);
        if (strcmp("exit", read_buf) == 0)
        {
            printf("%s exit, child %d terminated\n", pthread->socket, tid);
            close(connfd);
            pthread->connfd = -1;
            pthread_exit(NULL);
        }
    }
}

int main(void)
{
    int listenfd, connfd;
    struct sockaddr_in server_sockaddr;
    struct sockaddr_in client_addr;
    char buf[1024];
    char client_socket[128];
    socklen_t length;
    int idx;
    int opt = 1;

    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_port = htons(PORT);
    server_sockaddr.sin_addr.s_addr = inet_addr(IP);
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0)
    {
        perror("socket error");
        exit(1);
    }
    // 設置埠復用
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 
    if (bind(listenfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr)) < 0)
    {
        perror("bind error");
        exit(1);
    }
    if (listen(listenfd, 5) < 0)
    {
        perror("listen error");
        exit(1);
    }

    pthread_data_index_init();
    while (1)
    {
        // 接受來自客戶端的信息
        printf("accept start \n");
        memset(&client_addr, 0, sizeof(client_addr));
        length = sizeof(client_addr);
        if ((connfd = accept(listenfd, (struct sockaddr *)&client_addr, &length)) < 0)
        {
            if (errno == EINTR)
                continue;
            else
            {
                perror("accept error");
                exit(1);
            }
        }

        idx = pthread_data_index_find();
        if (idx == PTHREAD_MAX_SIZE)
        {
            printf("client connected upper limit, refused connect\n");
            close(connfd);
            continue;
        }

        memset(&client_socket, 0, sizeof(client_socket));
        printf("client addr:%s por:%d\n",
               inet_ntop(AF_INET, &client_addr.sin_addr, buf, sizeof(buf)),
               ntohs(client_addr.sin_port));
        snprintf(client_socket, sizeof(client_socket), "client socket (%s:%d)",
                 inet_ntop(AF_INET, &client_addr.sin_addr, buf, sizeof(buf)),
                 ntohs(client_addr.sin_port));
        g_pthread_data[idx].connfd = connfd;
        g_pthread_data[idx].cliaddr = client_addr;
        strcpy(g_pthread_data[idx].socket, client_socket);
        pthread_create(&(g_pthread_data[idx].pthread_id), NULL, pthread_handle, &(g_pthread_data[idx]));
        pthread_detach(g_pthread_data[idx].pthread_id);
    }
    close(listenfd);
    return EXIT_SUCCESS;
}

2-2 編譯運行


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、下載ThinkPHP6 在指定目錄(www目錄)下打開cmd,使用composer下載thinkphp6,命令後面的thinkphp6就是下載的目錄名,可以隨意修改。 composer create-project topthink/think thinkphp6 二、目錄結構 下載後,查看目錄 ...
  • hello,大家好啊,我是小樓。 最近在工作中對 Go 的 singleflight 包做了下增強,解決了一個性能問題,這裡記錄下,希望對你也有所幫助。 # singleflight 是什麼 singleflight 直接翻譯為”單(次)飛(行)“,它是對同一種請求的抑制,保證同一時刻相同的請求只有 ...
  • # 基本數據類型和引用數據類型在存儲上的區別 ## 基本數據類型有哪些 | 類型名稱 | 關鍵字 | 占用記憶體 | 取值範圍 | | | | | | | 位元組型 | byte | 1 位元組 | -128~127 | | 短整型 | short | 2 位元組 | -32768~32767 | | 整型 ...
  • **本文將為大家詳細講解Java中的List集合,這是我們進行開發時經常用到的知識點,也是大家在學習Java中很重要的一個知識點,更是我們在面試時有可能會問到的問題。** **文章較長,乾貨滿滿,建議大家收藏慢慢學習。文末有本文重點總結,主頁有全系列文章分享。技術類問題,歡迎大家和我們一起交流討論! ...
  • ## Lerna 備忘清單 lerna 是一種多包管理工具, 可以讓你在主項目下管理多個子項目,從而解決了多個包互相依賴,且發佈時需要手動維護多個包的問題,每個 package 都有自己的依賴項(package.json),能夠作為獨立的 npm package 發佈,只是源碼放在一起維護,公共包可 ...
  • Less 備忘清單 Less 是一門CSS預處理語言,它擴充了CSS語言,增加了諸如變數、混合(mixin)、函數等功能,讓CSS更易維護、方便製作主題、擴充。Less可以運行在Node.js或瀏覽器端。 Less開發速查清單本備忘單旨在快速理解 Less 所涉及的主要概念,顯示了它的常用方法使用清 ...
  • 01【熟悉】實際開發中的問題? 現在我們一個項目跑在一個tomcat裡面 當一個tomcat無法支持高的併發量時。可以使用多個tomcat 那麼這多個tomcat如何雲分配請求 |-nginx 02【熟悉】伺服器概述 1,目前常見的web伺服器 1,Apache(http://httpd.apach ...
  • 在前面的文章`《驅動開發:運用MDL映射實現多次通信》`LyShark教大家使用`MDL`的方式靈活的實現了內核態多次輸出結構體的效果,但是此種方法並不推薦大家使用原因很簡單首先內核空間比較寶貴,其次內核裡面不能分配太大且每次傳出的結構體最大不能超過`1024`個,而最終這些記憶體由於無法得到更好的釋... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...