国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

這篇具有很好參考價值的文章主要介紹了Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、問題引入

UNIX網絡編程:socket實現(xiàn)client/server通信 隨筆簡單介紹了TCP Server服務單客戶端的socket通信,但是并未涉及多客戶端通信。

對于網絡編程肯定涉及到多客戶端通信和并發(fā)編程 (指在同時有大量的客戶鏈接到同一服務器),故本隨筆補充這部分知識。

而且并發(fā)并發(fā)編程涉及到多進程、多線程,其中 fork()函數是Unix中派生新進程的唯一方法。

二、解決過程

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 <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define IP "10.8.198.227"
#define PORT 8887

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;
}

static int handle(int connect_fd, const char *socket)
{
    int recv_len, send_len;
    char read_buf[1024], send_buf[1024];

    for (;;)
    {
        memset(read_buf, 0, sizeof(read_buf));
        recv_len = read(connect_fd, read_buf, sizeof(read_buf));
        if (recv_len < 0)
        {
            printf("read error \n");
            break;
        }
        else if (recv_len == 0)
        {
            printf("%s close \n", socket);
            break;
        }
        printf("%s:%s(%d Byte)\n", socket, read_buf, recv_len);
        send_len = string_toupper(read_buf, strlen(read_buf), send_buf);
        write(connect_fd, send_buf, send_len);
        if (strcmp("exit", read_buf) == 0)
        {
            printf("%s close \n", socket);
            break;
        }
    }

    return 0;
}

static void sighandler(int signum)
{
    pid_t pid;
    while (1)
    {
        pid = waitpid(-1, NULL, WNOHANG);
        if (pid > 0)
        {
            printf("child %d terminated\n", pid);
        }
        if (pid == -1 || pid == 0)
            break;
    }
}

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 pid;

    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);
    }
    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);
    }
    // 注冊信號捕捉函數
    signal(SIGCHLD, sighandler);
    for (;;)
    {
        
        // 接受來自客戶端的信息
        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);
            }
        }
        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));
        pid = fork();
        if (pid == 0) // 子進程
        {
            close(listenfd);
            handle(connfd, client_socket);
            close(connfd);
            exit(1);
        }
        else if (pid < 0) // error
        {
            perror("fork error");
            close(connfd);
            exit(1);
        }
        else // 父進程
        {
            close(connfd);
        }
    }
    close(listenfd);
    return EXIT_SUCCESS;
}

2-2 client 代碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <unistd.h>

#define IP "10.8.198.227"
#define PORT 8887

static int handle(int connect_fd, const char *socket)
{
    char send_buf[1024], recv_buf[1024];
    int recv_len;
    for (;;)
    {
        memset(send_buf, 0, sizeof(send_buf));
        memset(recv_buf, 0, sizeof(recv_buf));
        fgets(send_buf, sizeof(send_buf), stdin);
        if (strlen(send_buf) <= 1)
            continue;
        if (send_buf[strlen(send_buf) - 1] == '\n')
            send_buf[strlen(send_buf) - 1] = '\0';
        write(connect_fd, send_buf, strlen(send_buf));
        if (strcmp("exit", send_buf) == 0)
            break;
        recv_len = read(connect_fd, recv_buf, sizeof(recv_buf));
        if (recv_len <= 0)
        {
            printf("read error or server closed, n==[%d] \n", recv_len);
            break;
        }
        printf("%s:%s(%d Byte)\n", socket, recv_buf, recv_len);
    }
    return 0;
}

int main(void)
{
    int sockfd;
    char buf[1024];
    struct sockaddr_in server_addr;
    char server_socket[128];

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr(IP);
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0)
    {
        printf("connect error \n");
        return -1;
    }
    printf("server addr:%s por:%d\n",
           inet_ntop(AF_INET, &server_addr.sin_addr, buf, sizeof(buf)),
             ntohs(server_addr.sin_port));
    snprintf(server_socket, sizeof(server_socket), "server socket (%s:%d)",
             inet_ntop(AF_INET, &server_addr.sin_addr, buf, sizeof(buf)),
             ntohs(server_addr.sin_port));
    handle(sockfd, server_socket);
    close(sockfd);

    return EXIT_SUCCESS;
}

2-3 運行測試

1、client 1 連接 server

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

?? 注意:server后臺查看TCP進程時,client的端口號可能不一致(原因是圖片是后補的)

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

2、client 2 連接 server

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

?? 注意:server后臺查看TCP進程時,client的端口號可能不一致(原因是圖片是后補的)

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

3、多客戶端與服務器通信

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

4、client 2斷開與 server的連接

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

client 2后臺查看TCP進程,發(fā)現(xiàn)TCP對應的套接字狀態(tài):TIME_WAIT,一段時間后,TCP對應的套接字進程才消失。

2-4 程序解讀

  • 客戶端

客戶端程序的主要功能是發(fā)送消息給服務器,并接受來自服務器的消息。

  • 服務器

服務器程序的主要功能是:
1)接受多個客戶端的連接,并為每個客戶端派生子進程負責通信,父進程負責接受客戶端的連接

2)接受來自不同客戶端的消息,并將消息加工發(fā)送給對應的客戶端

  • fork()
// pid_t 數據類型聲明在如下頭文件中
#include <sys/types.h>
// fork()原型聲明在如下頭文件中
#include <unistd.h>

/*
 * fork() 通過復制調用進程來創(chuàng)建一個新進程,子進程是父進程的一個副本
 * @return 若成功,返回值有效范圍:0~32767;否則失敗,返回值-1
 */
pid_t fork(void);

?? 任何子進程只有一個父進程,而且子進程總是可以通過 getppid() 獲取父進程的 pid。但是一個父進程可以擁有n (n >= 0) 個子進程

fork() 調用一次,它卻返回兩次。原因是子進程對父進程一個(邏輯)拷貝,它在調用進程(即父進程)中返回一次,返回值是新派生進程(即子進程)的pid,在子進程中又返回一次,返回值是0。

代碼中可以看到:

  pid = fork();
  if (pid == 0) // 子進程
  {
      close(listenfd);
      handle(connfd, client_socket);
      exit(1);
  }
  else if (pid < 0) // error
  {
      perror("fork error");
      close(connfd);
      exit(1);
  }
  else // 父進程
  {
      close(connfd);
  }

新的客戶端由子進程提供服務,同時關閉父進程的監(jiān)聽套接字listenfd,父進程需要關閉已連接套接字connfd

? question

父進程對connfd 調用close 沒有終止它與客戶的連接呢?
為了便于理解,我們必須知道每個文件或套接字都有一個引用計數。引用計數在文件表項中維護(APUE第58~59頁),它是當前打開著的引用該文件或套接字的描述符的個數。socket 返回后與listenfd 關聯(lián)的文件表項的引用計數值為1。accept 返回后與connfd 關聯(lián)的文件表項的引用計數值也為1。然而fork 返回后,這兩個描述符就在父進程與子進程間共享(也就是被復制),因此與這兩個套接字相關聯(lián)的文件表項各自的訪問計數值均為2。這么一來,當父進程關閉connfd時,它只是把相應的引用計數值從2減為1,當子進程關閉listenfd時,它只是把相應的引用計數值從2減為1。該套接字真正的清理和資源釋放要等到其引用計數值到達0時才發(fā)生。

fork() 的兩個典型用法:

1)一個進程創(chuàng)建一個自身的副本,這樣每個副本都可以在另一個副本執(zhí)行其他任務的同時處理各自的某個操作。例如網絡服務器

2)一個進程創(chuàng)建一個自身的副本,然后其中一個副本(通常為子進程)調用exec() 把自身替換為新的程序。例如shell程序

  • signal()
// signal()原型聲明在如下頭文件中
#include <signal.h>

typedef void (*sighandler_t)(int);

/*
 * @param signum 信號,可以根據對應信號,函數指針對信號進行處理
 * @param handler 函數指針
 * @return 返回值是一個函數指針
 */
sighandler_t signal(int signum, sighandler_t handler);

信號機制是進程之間相互傳遞消息的一種方法,信號全稱為軟中斷信號,也有人稱作軟中斷。從它的命名可以看出,它的實質和使用很象中斷。所以,信號可以說是進程控制的一部分

隨筆中:signal(SIGCHLD, sighandler); ,其中 SIGCHLD 表示:子進程狀態(tài)發(fā)生變化。通過signal函數,一旦服務器和客戶端通信異常,即可捕捉服務器中和客戶端通信的子進程。

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

三、反思總結

服務器與多客戶端通信,涉及到多進程的處理,子進程結束,如何監(jiān)控子進程的pid。

?? 問題思考:隨筆例子中,若服務器子進程結束,客戶端是無感知的,僅當客戶端往服務器再次發(fā)送數據才能得知服務器子進程結束。那么如何在服務器子進程一結束,客戶端進程立馬能知道呢?(待解決。。。)

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信

四、參考引用

UNIX網絡編程 卷1:套接字聯(lián)網API 第3版

Unix網絡編程學習筆記文章來源地址http://www.zghlxwxcb.cn/news/detail-448901.html

到了這里,關于Linux網絡編程:socket & fork()多進程 實現(xiàn)clients/server通信的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • UNIX網絡編程:socket實現(xiàn)client/server通信

    UNIX網絡編程:socket實現(xiàn)client/server通信

    閱讀 UNIX網絡編程 卷1:套接字聯(lián)網API 第3版 的前4個章節(jié),覺得有必要對書籍上的源碼案例進行復現(xiàn),并推敲TCP的C/S通信過程。 ?? 測試環(huán)境:CentOS7.6 x64 編譯server.c 和 client.c gcc server.c -g -std=gnu99 -o server 和 gcc client.c -g -std=gnu99 -o client 運行測試: ?? server.c僅僅實現(xiàn)對單個客戶

    2024年02月06日
    瀏覽(24)
  • Linux網絡編程:Socket套接字編程(Server服務器 Client客戶端)

    Linux網絡編程:Socket套接字編程(Server服務器 Client客戶端)

    文章目錄: 一:定義和流程分析 1.定義 2.流程分析? 3.網絡字節(jié)序 二:相關函數? IP地址轉換函數inet_pton inet_ntop(本地字節(jié)序 網絡字節(jié)序) socket函數(創(chuàng)建一個套接字) bind函數(給socket綁定一個服務器地址結構(IP+port)) listen函數(設置最大連接數或者說能同時進行三次握手的最

    2024年02月12日
    瀏覽(35)
  • UNIX網絡編程:socket & pthread_create()多線程 實現(xiàn)clients/server通信

    UNIX網絡編程:socket & pthread_create()多線程 實現(xiàn)clients/server通信

    UNIX網絡編程:socket fork()多進程 實現(xiàn)clients/server通信 隨筆介紹了通過fork()多進程實現(xiàn)了服務器與多客戶端通信。但除了多進程能實現(xiàn)之外,多線程也是一種實現(xiàn)方式。 重要的是,多進程和多線程是涉及操作系統(tǒng)層次。隨筆不僅要利用pthread_create()實現(xiàn)多線程編程,也要理解線

    2024年02月06日
    瀏覽(25)
  • 「網絡編程」第二講:socket套接字(四 - 完結)_ Linux任務管理與守護進程 | TCP協(xié)議通訊流程

    「網絡編程」第二講:socket套接字(四 - 完結)_ Linux任務管理與守護進程 | TCP協(xié)議通訊流程

    「前言」文章是關于網絡編程的socket套接字方面的,上一篇是網絡編程socket套接字(三),這篇續(xù)上篇文章的內容,下面開始講解!? 「歸屬專欄」網絡編程 「主頁鏈接」個人主頁 「筆者」楓葉先生(fy) 「楓葉先生有點文青病」「句子分享」 Time?goes?on?and?on,?never?to?an?

    2024年02月10日
    瀏覽(47)
  • Linux socket網絡編程實戰(zhàn)(tcp)實現(xiàn)雙方聊天

    Linux socket網絡編程實戰(zhàn)(tcp)實現(xiàn)雙方聊天

    在上節(jié)已經系統(tǒng)介紹了大致的流程和相關的API,這節(jié)就開始寫代碼! 回顧上節(jié)的流程: 創(chuàng)建一個NET文件夾 來存放網絡編程相關的代碼: 這部分先實現(xiàn)服務器的連接部分的代碼并進行驗證 server1.c: 代碼驗證: 先編譯并運行這部分代碼: 可見,此時沒有客戶端進行連接,程

    2024年02月03日
    瀏覽(22)
  • 【Linux網絡】網絡編程套接字 -- 基于socket實現(xiàn)一個簡單UDP網絡程序

    【Linux網絡】網絡編程套接字 -- 基于socket實現(xiàn)一個簡單UDP網絡程序

    我們把數據從A主機發(fā)送到B主機,是目的嗎?不是,真正通信的不是這兩個機器!其實是這兩臺機器上面的軟件(人) 數據有 IP(公網) 標識一臺唯一的主機 ,用誰來標識各自主機上客戶或者服務進程的唯一性呢? 為了更好的表示一臺主機上服務進程的唯一性,我們采用 端口號

    2024年02月12日
    瀏覽(848)
  • Linux網絡編程:Socket服務器和客戶端實現(xiàn)雙方通信

    Linux網絡編程:Socket服務器和客戶端實現(xiàn)雙方通信

    目錄 一,什么是網絡編程 二,為什么使用端口號 三,TCP協(xié)議與UDP協(xié)議 ①TCP(傳輸控制協(xié)議) ②UDP(用戶數據報協(xié)議,User Data Protocol) ③總結歸納 四,Socket服務器和客戶端的開發(fā)流程 五,服務器和客戶端相關API說明 ①socket()函數 ②bind()函數 ③listen()函數 ④accept()函數 ⑤客戶端

    2024年02月11日
    瀏覽(34)
  • 網絡編程day1——進程間通信-socket套接字

    ????????基本特征:socket是一種接口技術,被抽象了一種文件操作,可以讓同一計算機中的不同進程之間通信,也可以讓不同計算機中的進程之間通信(網絡通信) ? ? ? ? 進程A? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????? 進程B ? ? 創(chuàng)建socket對象

    2024年02月10日
    瀏覽(106)
  • Linux網絡編程——C++實現(xiàn)進程間TCP/IP通信

    地址接口 1、通用地址接口 共16字節(jié) = 2字節(jié)地址類型 + 14字節(jié)地址數據 2、自定義地址接口 地址轉換 1、需要將點分字符串ip轉化為程序ip,使用inet_addr函數: 2、字節(jié)序轉換 地址接口配置中的端口需要字節(jié)序轉換,網絡規(guī)定使用大端字節(jié)序。 地址接口配置 1、socket:創(chuàng)建套接

    2024年02月20日
    瀏覽(33)
  • 【實戰(zhàn)項目】網絡編程:在Linux環(huán)境下基于opencv和socket的人臉識別系統(tǒng)--C++實現(xiàn)

    【實戰(zhàn)項目】網絡編程:在Linux環(huán)境下基于opencv和socket的人臉識別系統(tǒng)--C++實現(xiàn)

    這里我們會實現(xiàn)一個項目:在linux操作系統(tǒng)下基于OpenCV和Socket的人臉識別系統(tǒng)。 目錄 ??前言 ??一、項目介紹 ??二、項目分工 ??三、項目難題 ??四、實現(xiàn)細節(jié) ??4.1?關鍵程序 ??4.2?運行結果 ??五、程序分析 ??5.1 wkcv.link ??5.2?客戶端client.cpp ??5.3?服務端server.cpp 項目

    2024年03月12日
    瀏覽(14)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包