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

TCP服務(wù)器的演變過程:IO多路復(fù)用機制select實現(xiàn)TCP服務(wù)器

這篇具有很好參考價值的文章主要介紹了TCP服務(wù)器的演變過程:IO多路復(fù)用機制select實現(xiàn)TCP服務(wù)器。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、前言

手把手教你從0開始編寫TCP服務(wù)器程序,體驗開局一塊磚,大廈全靠壘。

為了避免篇幅過長使讀者感到乏味,對【TCP服務(wù)器的開發(fā)】進行分階段實現(xiàn),一步步進行優(yōu)化升級。
本節(jié),在上一章節(jié)的基礎(chǔ)上,將并發(fā)的實現(xiàn)改為IO多路復(fù)用機制,使用select管理每個新接入的客戶端連接,實現(xiàn)發(fā)送和接收。

二、新增使用API函數(shù)

2.1、select()函數(shù)

函數(shù)原型:

#include <sys/types.h>
#include <unistd.h>

int select(int maxfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

select函數(shù)共有5個參數(shù),其中參數(shù):

  • maxfds:監(jiān)視對象文件描述符數(shù)量。
  • readset:將所有關(guān)注“是否存在待讀取數(shù)據(jù)”的文件描述符注冊到fd_set變量,并傳遞其地址值。
  • writeset: 將所有關(guān)注“是否可傳輸無阻塞數(shù)據(jù)”的文件描述符注冊到fd_set變量,并傳遞其地址值。
  • exceptset:將所有關(guān)注“是否發(fā)生異常”的文件描述符注冊到fd_set變量,并傳遞其地址值。
  • timeout:調(diào)用select后,為防止陷入無限阻塞狀態(tài),傳遞超時信息。

返回值:

  • 錯誤返回-1。
  • 超時返回0。

當(dāng)關(guān)注的事件返回時,返回大于0的值,該值是發(fā)生事件的文件描述符數(shù)。

2.2、FD_*系列函數(shù)

函數(shù)原型:

#include <sys/types.h>
#include <unistd.h>

void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

(1)FD_CLR函數(shù)用于將fd從set集合中清除,即不監(jiān)控該fd的事件。

(2)FD_SET函數(shù)用于將fd添加到set集合中,監(jiān)控其事件。

(3)FD_ZERO函數(shù)用于將set集合重置。

(4)FD_ISSET函數(shù)用于判斷set集合中的fd是否有事件(讀、寫、錯誤)。

三、實現(xiàn)步驟

什么是IO多路復(fù)用?通俗的講就是一個線程,通過記錄IO流的狀態(tài)來管理多個IO。解決創(chuàng)建多個進程處理IO流導(dǎo)致CPU占用率高的問題。

select是io多路復(fù)用的一種方式,其他的還有poll、epoll等。
TCP服務(wù)器的演變過程:IO多路復(fù)用機制select實現(xiàn)TCP服務(wù)器,Linux網(wǎng)絡(luò)設(shè)計,tcp/ip,服務(wù)器,網(wǎng)絡(luò),select,c語言,網(wǎng)絡(luò)協(xié)議,linux

(1)創(chuàng)建socket。

int listenfd=socket(AF_INET,SOCK_STREAM,0);
if(listenfd==-1){
    printf("errno = %d, %s\n",errno,strerror(errno));
    return SOCKET_CREATE_FAILED;
}

(2)綁定地址。

struct sockaddr_in server;
memset(&server,0,sizeof(server));

server.sin_family=AF_INET;
server.sin_addr.s_addr=htonl(INADDR_ANY);
server.sin_port=htons(LISTEN_PORT);

if(-1==bind(listenfd,(struct sockaddr*)&server,sizeof(server))){
    printf("errno = %d, %s\n",errno,strerror(errno));
    close(listenfd);
    return SOCKET_BIND_FAILED;
}

(3)設(shè)置監(jiān)聽。

if(-1==listen(listenfd,BLOCK_SIZE)){
   printf("errno = %d, %s\n",errno,strerror(errno));
   close(listenfd);
   return SOCKET_LISTEN_FAILED;
}

(4)初始化可讀文件描述符集合,將監(jiān)聽套接字加入集合。

fd_set writefds,readfds,wset,rset;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(listenfd,&readfds);

(5)從可讀文件描述符集合中選擇一個就緒的套接字。

wset=writefds;
rset=readfds;

// 從可讀文件描述符集合中選擇就緒的套接字
int nready=select(maxfd+1,&rset,&wset,NULL,NULL);
        
if(nready==-1)
{
    printf("select errno = %d, %s\n",errno,strerror(errno));
    continue;
}

(6)如果監(jiān)聽套接字有新連接請求,處理新連接。

struct sockaddr_in client;
memset(&client,0,sizeof(client));
socklen_t len=sizeof(client);

int clientfd=accept(listenfd,(struct sockaddr*)&client,&len);
if(clientfd==-1){
    printf("accept errno = %d, %s\n",errno,strerror(errno));
}
else{
    printf("accept successdul, clientfd = %d\n",clientfd);
    // 將新套接字加入可讀文件描述符集合
    FD_SET(clientfd,&readfds);
    if(clientfd>maxfd)
        maxfd=clientfd;
}

(7)處理客戶端發(fā)來的數(shù)據(jù)和發(fā)送數(shù)據(jù)到客戶端。

        int i=0;
        for(i=listenfd+1;i<=maxfd;i++)
        {
            if(FD_ISSET(i,&rset))
            {
                printf("recv fd=%d\n",i);
                ret=recv(i,buf,BUFFER_LENGTH,0);
                if(ret==0) {
                    // 客戶端斷開連接
                    printf("connection dropped\n");
                    // 從可讀文件描述符集合中移除該套接字
                    FD_CLR(i,&readfds);
                    close(i);
                }
                else if(ret>0)
                {
                    printf("fd=%d recv --> %s\n",i,buf);
                    FD_CLR(i,&readfds);
                    FD_SET(i,&writefds);
                    
                }
                
            }
            else if(FD_ISSET(i,&wset))
            {
                printf("send to fd=%d\n",i);
                ret=send(i,buf,ret,0);
                if(ret==-1)
                {
                    printf("send() errno = %d, %s\n",errno,strerror(errno));
                }
                FD_CLR(i,&writefds);
                FD_SET(i,&readfds);
            }
        }

四、完整代碼

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

#include <errno.h>
#include <string.h>
#include <unistd.h>

#include <sys/select.h>

#define LISTEN_PORT     9999
#define BLOCK_SIZE      10
#define BUFFER_LENGTH   1024

enum ERROR_CODE{
    SOCKET_CREATE_FAILED=-1,
    SOCKET_BIND_FAILED=-2,
    SOCKET_LISTEN_FAILED=-3,
    SOCKET_ACCEPT_FAILED=-4,
    SOCKET_SELECT_FAILED=-5
};


int main(int argc,char **argv)
{
    // 1.
    int listenfd=socket(AF_INET,SOCK_STREAM,0);
    if(listenfd==-1){
        printf("errno = %d, %s\n",errno,strerror(errno));
        return SOCKET_CREATE_FAILED;
    }

    // 2.
    struct sockaddr_in server;
    memset(&server,0,sizeof(server));

    server.sin_family=AF_INET;
    server.sin_addr.s_addr=htonl(INADDR_ANY);
    server.sin_port=htons(LISTEN_PORT);

    if(-1==bind(listenfd,(struct sockaddr*)&server,sizeof(server))){
        printf("errno = %d, %s\n",errno,strerror(errno));
        close(listenfd);
        return SOCKET_BIND_FAILED;
    }

    // 3.
    if(-1==listen(listenfd,BLOCK_SIZE)){
        printf("errno = %d, %s\n",errno,strerror(errno));
        close(listenfd);
        return SOCKET_LISTEN_FAILED;
    }

    printf("listen port: %d\n",LISTEN_PORT);
    
    fd_set writefds,readfds,wset,rset;
    FD_ZERO(&readfds);
    FD_ZERO(&writefds);
    FD_SET(listenfd,&readfds);

    char buf[BUFFER_LENGTH]={0};
    int ret=0;
    int maxfd=listenfd;
    while(1)
    {
        wset=writefds;
        rset=readfds;

        // 從可讀文件描述符集合中選擇就緒的套接字
        int nready=select(maxfd+1,&rset,&wset,NULL,NULL);
        
        if(nready==-1)
        {
            printf("select errno = %d, %s\n",errno,strerror(errno));
            continue;
        }

        // 如果監(jiān)聽套接字有新連接請求,處理新連接
        if(FD_ISSET(listenfd,&rset))
        {
            // 4.
            printf("accept , listenfd = %d\n",listenfd);
            struct sockaddr_in client;
            memset(&client,0,sizeof(client));
            socklen_t len=sizeof(client);
            
            int clientfd=accept(listenfd,(struct sockaddr*)&client,&len);
            if(clientfd==-1){
                printf("accept errno = %d, %s\n",errno,strerror(errno));
            }
            else{
                printf("accept successdul, clientfd = %d\n",clientfd);
                // 將新套接字加入可讀文件描述符集合
                FD_SET(clientfd,&readfds);
                if(clientfd>maxfd)
                    maxfd=clientfd;
            }

            
        }
        printf("listenfd=%d.maxfd=%d\n",listenfd,maxfd);
        int i=0;
        for(i=listenfd+1;i<=maxfd;i++)
        {
            if(FD_ISSET(i,&rset))
            {
                printf("recv fd=%d\n",i);
                ret=recv(i,buf,BUFFER_LENGTH,0);
                if(ret==0) {
                    // 客戶端斷開連接
                    printf("connection dropped\n");
                    // 從可讀文件描述符集合中移除該套接字
                    FD_CLR(i,&readfds);
                    close(i);
                }
                else if(ret>0)
                {
                    printf("fd=%d recv --> %s\n",i,buf);
                    FD_CLR(i,&readfds);
                    FD_SET(i,&writefds);
                    
                }
                
            }
            else if(FD_ISSET(i,&wset))
            {
                printf("send to fd=%d\n",i);
                ret=send(i,buf,ret,0);
                if(ret==-1)
                {
                    printf("send() errno = %d, %s\n",errno,strerror(errno));
                }
                FD_CLR(i,&writefds);
                FD_SET(i,&readfds);
            }
        }
    }

    
    close(listenfd);

    return 0;
}

編譯命令:

gcc -o server server.c

五、TCP客戶端

5.1、自己實現(xiàn)一個TCP客戶端

自己實現(xiàn)一個TCP客戶端連接TCP服務(wù)器的代碼:

#include <stdio.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <errno.h>
#include <string.h>

#include <unistd.h>
#include <stdlib.h>

#define BUFFER_LENGTH   1024

enum ERROR_CODE{
    SOCKET_CREATE_FAILED=-1,
    SOCKET_CONN_FAILED=-2,
    SOCKET_LISTEN_FAILED=-3,
    SOCKET_ACCEPT_FAILED=-4
};

int main(int argc,char** argv)
{
    if(argc<3)
    {
        printf("Please enter the server IP and port.");
        return 0;
    }
    printf("connect to %s, port=%s\n",argv[1],argv[2]);

    int connfd=socket(AF_INET,SOCK_STREAM,0);
    if(connfd==-1)
    {
        printf("errno = %d, %s\n",errno,strerror(errno));
        return SOCKET_CREATE_FAILED;

    }
    struct sockaddr_in serv;
    serv.sin_family=AF_INET;
    serv.sin_addr.s_addr=inet_addr(argv[1]);
    serv.sin_port=htons(atoi(argv[2]));
    socklen_t len=sizeof(serv);
    int rwfd=connect(connfd,(struct sockaddr*)&serv,len);
    if(rwfd==-1)
    {
        printf("errno = %d, %s\n",errno,strerror(errno));
        close(rwfd);
        return SOCKET_CONN_FAILED;
    }
    int ret=1;
    while(ret>0)
    {
        char buf[BUFFER_LENGTH]={0};
        printf("Please enter the string to send:\n");
        scanf("%s",buf);
        send(connfd,buf,strlen(buf),0);

        memset(buf,0,BUFFER_LENGTH);
        printf("recv:\n");
        ret=recv(connfd,buf,BUFFER_LENGTH,0);
        printf("%s\n",buf);
        
    }
    close(rwfd);
    return 0;
}

編譯:

gcc -o client client.c

5.2、Windows下可以使用NetAssist的網(wǎng)絡(luò)助手工具

TCP服務(wù)器的演變過程:IO多路復(fù)用機制select實現(xiàn)TCP服務(wù)器,Linux網(wǎng)絡(luò)設(shè)計,tcp/ip,服務(wù)器,網(wǎng)絡(luò),select,c語言,網(wǎng)絡(luò)協(xié)議,linux
下載地址:http://old.tpyboard.com/downloads/NetAssist.exe

小結(jié)

至此,我們實現(xiàn)了一個使用IO多路復(fù)用機制實現(xiàn)的服務(wù)器,這時的TCP服務(wù)器可以使用一個線程就能處理多個客戶端連接。通過記錄IO流的狀態(tài)來管理多個IO,解決創(chuàng)建多個進程處理IO流導(dǎo)致CPU占用率高的問題。

我們總結(jié)一下select的使用流程:

1、定義io管理狀態(tài)變量:fd_set rfds,wfds;

2、初始化變量:FD_ZERO();

3、設(shè)置io流狀態(tài),最初只有監(jiān)聽的fd,將其設(shè)置:FD_SET(listenfd,rfds);

4、在循環(huán)中select。

5、FD_ISSET()判斷端口是否有連接。

6、FD_ISSET()判斷可讀、可寫狀態(tài)。

select是io多路復(fù)用的一種方式,其他的還有poll、epoll等。下一章節(jié)我們將使用更高效的IO多路復(fù)用器epoll來實現(xiàn)TCP服務(wù)器。
TCP服務(wù)器的演變過程:IO多路復(fù)用機制select實現(xiàn)TCP服務(wù)器,Linux網(wǎng)絡(luò)設(shè)計,tcp/ip,服務(wù)器,網(wǎng)絡(luò),select,c語言,網(wǎng)絡(luò)協(xié)議,linux文章來源地址http://www.zghlxwxcb.cn/news/detail-773149.html

到了這里,關(guān)于TCP服務(wù)器的演變過程:IO多路復(fù)用機制select實現(xiàn)TCP服務(wù)器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • linux并發(fā)服務(wù)器 —— IO多路復(fù)用(八)

    linux并發(fā)服務(wù)器 —— IO多路復(fù)用(八)

    半關(guān)閉只能實現(xiàn)數(shù)據(jù)單方向的傳輸;當(dāng)TCP 接中A向 B 發(fā)送 FIN 請求關(guān)閉,另一端 B 回應(yīng)ACK 之后 (A 端進入 FIN_WAIT_2 狀態(tài)),并沒有立即發(fā)送 FIN 給 A,A 方處于半連接狀態(tài) (半開關(guān)),此時 A 可以接收 B 發(fā)送的數(shù)據(jù),但是 A 已經(jīng)不能再向 B 發(fā)送數(shù)據(jù) close不會影響到其他進程,shutdown會

    2024年02月09日
    瀏覽(22)
  • 【高并發(fā)服務(wù)器 02】——線程池與IO多路復(fù)用

    線程池的好處 :所有的池都是為了事先把資源準(zhǔn)備好,在后續(xù)用的時候可以更加方便的拿到這個資源—— 不用去申請、釋放資源 什么時候用線程池 ? IO事務(wù)并發(fā)較高 :人在杭州,但是數(shù)據(jù)庫在北京,想要查詢數(shù)據(jù)庫,需要通過互聯(lián)網(wǎng)建立TCP三次握手,頻繁地創(chuàng)建和銷毀線

    2024年03月23日
    瀏覽(24)
  • Linux多路IO復(fù)用技術(shù)——epoll詳解與一對多服務(wù)器實現(xiàn)

    Linux多路IO復(fù)用技術(shù)——epoll詳解與一對多服務(wù)器實現(xiàn)

    本文詳細(xì)介紹了Linux中epoll模型的優(yōu)化原理和使用方法,以及如何利用epoll模型實現(xiàn)簡易的一對多服務(wù)器。通過對epoll模型的優(yōu)化和相關(guān)接口的解釋,幫助讀者理解epoll模型的工作原理和優(yōu)缺點,同時附帶代碼實現(xiàn)和圖解說明。

    2024年02月05日
    瀏覽(27)
  • TCP服務(wù)器的演變過程:多進程實現(xiàn)一對多的TCP服務(wù)器

    TCP服務(wù)器的演變過程:多進程實現(xiàn)一對多的TCP服務(wù)器

    手把手教你從0開始編寫TCP服務(wù)器程序,體驗開局一塊磚,大廈全靠壘。 為了避免篇幅過長使讀者感到乏味,對【TCP服務(wù)器的開發(fā)】進行分階段實現(xiàn),一步步進行優(yōu)化升級。本節(jié)在上一章節(jié)的基礎(chǔ)上,改為多進程方式實現(xiàn)TCP服務(wù)器,為每個新接入的客戶端分配進程,實現(xiàn)一個

    2024年02月04日
    瀏覽(22)
  • TCP服務(wù)器的演變過程:揭秘使用多線程實現(xiàn)一對多的TCP服務(wù)器

    TCP服務(wù)器的演變過程:揭秘使用多線程實現(xiàn)一對多的TCP服務(wù)器

    手把手教你從0開始編寫TCP服務(wù)器程序,體驗開局一塊磚,大廈全靠壘。 為了避免篇幅過長使讀者感到乏味,對【TCP服務(wù)器的開發(fā)】進行分階段實現(xiàn),一步步進行優(yōu)化升級。本節(jié)在上一章節(jié)的基礎(chǔ)上,添加多線程,為每個新接入的客戶端分配線程,實現(xiàn)一個服務(wù)器程序處理多

    2024年02月04日
    瀏覽(26)
  • 【TCP服務(wù)器的演變過程】編寫第一個TCP服務(wù)器:實現(xiàn)一對一的連接通信

    【TCP服務(wù)器的演變過程】編寫第一個TCP服務(wù)器:實現(xiàn)一對一的連接通信

    手把手教你從0開始編寫TCP服務(wù)器程序,體驗 開局一塊磚,大廈全靠壘 。 為了避免篇幅過長使讀者感到乏味,對【TCP服務(wù)器的開發(fā)】進行分階段實現(xiàn),一步步進行優(yōu)化升級。 函數(shù)原型: 這個函數(shù)建立一個協(xié)議族、協(xié)議類型、協(xié)議編號的socket文件描述符。如果函數(shù)調(diào)用成功,

    2024年02月03日
    瀏覽(23)
  • TCP服務(wù)器的演變過程:C++使用libevent庫開發(fā)服務(wù)器程序

    TCP服務(wù)器的演變過程:C++使用libevent庫開發(fā)服務(wù)器程序

    手把手教你從0開始編寫TCP服務(wù)器程序,體驗開局一塊磚,大廈全靠壘。 為了避免篇幅過長使讀者感到乏味,對【TCP服務(wù)器的開發(fā)】進行分階段實現(xiàn),一步步進行優(yōu)化升級。 在上一章節(jié)介紹了如何使用epoll構(gòu)建reactor網(wǎng)絡(luò)模型開發(fā)高效的服務(wù)器,有了上一節(jié)的基礎(chǔ),本節(jié)將介紹

    2024年01月23日
    瀏覽(15)
  • TCP服務(wù)器的演變過程:使用epoll構(gòu)建reactor網(wǎng)絡(luò)模型實現(xiàn)百萬級并發(fā)(詳細(xì)代碼)

    TCP服務(wù)器的演變過程:使用epoll構(gòu)建reactor網(wǎng)絡(luò)模型實現(xiàn)百萬級并發(fā)(詳細(xì)代碼)

    手把手教你從0開始編寫TCP服務(wù)器程序,體驗開局一塊磚,大廈全靠壘。 為了避免篇幅過長使讀者感到乏味,對【TCP服務(wù)器的開發(fā)】進行分階段實現(xiàn),一步步進行優(yōu)化升級。 本節(jié),在上一章節(jié)介紹了如何使用epoll開發(fā)高效的服務(wù)器,本節(jié)將介紹使用epoll構(gòu)建reactor網(wǎng)絡(luò)模型,實

    2024年02月01日
    瀏覽(30)
  • 【高并發(fā)網(wǎng)絡(luò)通信架構(gòu)】引入IO多路復(fù)用(select,poll,epoll)實現(xiàn)高并發(fā)tcp服務(wù)端

    【高并發(fā)網(wǎng)絡(luò)通信架構(gòu)】引入IO多路復(fù)用(select,poll,epoll)實現(xiàn)高并發(fā)tcp服務(wù)端

    目錄 一,往期文章 二,基本概念 IO多路復(fù)用 select 模型 poll 模型 epoll 模型 select,poll,epoll 三者對比 三,函數(shù)清單 1.select 方法 2.fd_set 結(jié)構(gòu)體 3.poll 方法 4.struct pollfd 結(jié)構(gòu)體 5.epoll_create 方法 6.epoll_ctl 方法 7.epoll_wait 方法 8.struct epoll_event 結(jié)構(gòu)體 四,代碼實現(xiàn) select 操作流程 s

    2024年02月12日
    瀏覽(31)
  • epoll多路復(fù)用_并發(fā)服務(wù)器

    應(yīng)用程序: 驅(qū)動程序:

    2024年02月15日
    瀏覽(20)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包