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
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...