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

【Linux網(wǎng)絡(luò)編程】TCP并發(fā)服務(wù)器的實(shí)現(xiàn)(IO多路復(fù)用select)

這篇具有很好參考價(jià)值的文章主要介紹了【Linux網(wǎng)絡(luò)編程】TCP并發(fā)服務(wù)器的實(shí)現(xiàn)(IO多路復(fù)用select)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

一、服務(wù)器模型

1.1 服務(wù)器概念

服務(wù)器模型主要分為兩種,循環(huán)服務(wù)器并發(fā)服務(wù)器。

循環(huán)服務(wù)器
在同一時(shí)間只能處理一個(gè)客戶端的請(qǐng)求。
并發(fā)服務(wù)器
在同一時(shí)間內(nèi)能同時(shí)處理多個(gè)客戶端的請(qǐng)求。

TCP的服務(wù)器默認(rèn)的就是一個(gè)循環(huán)服務(wù)器,原因是有兩個(gè)阻塞 accept函數(shù) 和recv函數(shù) 之間會(huì)相互影響。
UDP的服務(wù)器默認(rèn)的就是一個(gè)并發(fā)服務(wù)器,因?yàn)橹挥幸粋€(gè)阻塞的 recvfrom函數(shù)。

1.2 TCP并發(fā)服務(wù)器的意義

在有些應(yīng)用場(chǎng)景下,我們既要保證數(shù)據(jù)可靠,又要支持并發(fā)
這就需要用到TCP并發(fā)服務(wù)器。

1.3 實(shí)現(xiàn)TCP并發(fā)服務(wù)器的方式

  1. 使用多路IO復(fù)用實(shí)現(xiàn)TCP并發(fā)服務(wù)器(常用)
  2. 使用多進(jìn)程實(shí)現(xiàn)TCP并發(fā)服務(wù)器
  3. 使用多線程實(shí)現(xiàn)TCP并發(fā)服務(wù)器

本次我們學(xué)習(xí)第一個(gè)方式,使用多路IO復(fù)用(select函數(shù))實(shí)現(xiàn)TCP并發(fā)服務(wù)器的實(shí)現(xiàn),在后續(xù)博客中我們會(huì)依次講解其他實(shí)現(xiàn)方式。
感興趣可以收藏加關(guān)注哦。

二、使用IO多路復(fù)用實(shí)現(xiàn)TCP并發(fā)服務(wù)器優(yōu)勢(shì)

對(duì)于實(shí)際開發(fā)過程中:
如果使用多進(jìn)程實(shí)現(xiàn)TCP并發(fā)服務(wù)器,并發(fā)量大的時(shí)候,對(duì)系統(tǒng)的資源占用量也會(huì)很大。

如果使用多線程,業(yè)務(wù)邏輯復(fù)雜的時(shí)候,又涉及到臨近資源訪問的問題
比較好的方式是使用多路IO復(fù)用實(shí)現(xiàn)TCP并發(fā)服務(wù)器。

三、select函數(shù)

功能:
	實(shí)現(xiàn)IO多路復(fù)用
頭文件:
	 #include <sys/select.h>
函數(shù)原型:
	int select(int nfds, fd_set *readfds, fd_set *writefds,
               fd_set *exceptfds, struct timeval *timeout);
參數(shù)
	@nfds:監(jiān)視的最大文件描述符+1
	@readfds:要監(jiān)視的讀文件描述符集合,如果不關(guān)心,可以傳NULL
	@writefds:要監(jiān)視的寫文件描述符集合,如果不關(guān)心,可以傳NULL
	@exceptfds:要監(jiān)視的異常的文件描述符集合,如果不關(guān)心,可以傳NULL
	(一般我們只關(guān)心readfds)
	@timeout:超時(shí)時(shí)間
		為0時(shí)非阻塞
		為NULL時(shí)永久阻塞
		為結(jié)構(gòu)體時(shí)阻塞一定時(shí)間
返回值:
	成功  返回就緒文件描述符的個(gè)數(shù)
	失敗  返回-1,置位錯(cuò)誤碼
	超時(shí)  返回0

	
void FD_CLR(int fd, fd_set *set);
功能:
	刪除集合中的文件描述符
參數(shù):
	@fd:文件描述符
	@set:構(gòu)建要監(jiān)視的文件描述符集合
	
int  FD_ISSET(int fd, fd_set *set);
功能:
	判斷文件描述符是否在集合中
參數(shù):
	@fd:文件描述符
	@set:構(gòu)建要監(jiān)視的文件描述符集合
返回值:
	為0時(shí)不在里面
	非0時(shí)在里面
	
void FD_SET(int fd, fd_set *set);
功能:
	將文件描述符添加到集合中
參數(shù):
	@fd:文件描述符
	@set:構(gòu)建要監(jiān)視的文件描述符集合
	
void FD_ZERO(fd_set *set);
功能:
	清空集合
參數(shù):
	@set:構(gòu)建要監(jiān)視的文件描述符集合

注意:

  1. select只能監(jiān)視小于 FD_SETSIZE(1024) 的文件描述符。
  2. select函數(shù)在返回時(shí)會(huì)將沒有就緒的文件描述符在表中擦除,
    所以,在循環(huán)中調(diào)用select時(shí),每次需要重新填充集合。

四、TCP并發(fā)服務(wù)器的構(gòu)建

4.1 創(chuàng)建套接字

使用socket函數(shù)創(chuàng)建IPV4、TCP套接字

	int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        ERRLOG("socket error");
    }

4.2 填寫服務(wù)器網(wǎng)絡(luò)信息結(jié)構(gòu)體

	struct sockaddr_in serviceaddr;
    memset(&serviceaddr, 0, sizeof(serviceaddr));
    serviceaddr.sin_family = AF_INET;
    serviceaddr.sin_addr.s_addr = inet_addr(argv[1]);
    serviceaddr.sin_port = htons(atoi(argv[2]));
    socklen_t serviceaddr_len = sizeof(serviceaddr);

4.3 將服務(wù)器網(wǎng)絡(luò)信息結(jié)構(gòu)體與套接字綁定

	if (bind(sockfd, (struct sockaddr *)&serviceaddr, serviceaddr_len) == -1)
    {
        ERRLOG("bind error");
    }

4.4 將套接字設(shè)置為被動(dòng)監(jiān)聽狀態(tài)

	if (listen(sockfd, 5) == -1)
    {
        ERRLOG("listen error");
    }

4.5 創(chuàng)建文件描述符集合母本和子本并進(jìn)行清空操作

	fd_set readfds;
    FD_ZERO(&readfds);
    fd_set readfds_msg;
    FD_ZERO(&readfds_msg);

4.6 將sockfd添加進(jìn)入集合內(nèi),并更新最大文件描述符

	FD_SET(sockfd, &readfds);
    max_fd = max_fd > sockfd ? max_fd : sockfd;

4.7 循環(huán)實(shí)現(xiàn)內(nèi)部功能偽代碼

 	while(1){
        select();
        //遍歷文件描述符集合
        for(){
            if(sockfd就緒了){
                //說明有新的客戶端建立連接了
                acceptfd = accept();
                將acceptfd加入到readfds中
                更新最大文件描述符
            }else{
                //說明有客戶端發(fā)來數(shù)據(jù)了
                recv();
                //如果recv返回0了 需要將當(dāng)前的客戶端的acceptfd在
                //readfds中刪除,后續(xù)就不再監(jiān)視它了
                strcat();
                send();
            }
        }
    }

五、客戶端的構(gòu)建

5.1步驟一和二和4.1,4.2一樣

5.2 嘗試與服務(wù)器建立連接

	if(-1 == connect(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len)){
        ERRLOG("connect error");
    }

5.3 內(nèi)部功能實(shí)現(xiàn)偽代碼

	while(1){
        //從終端獲取數(shù)據(jù)寫入buff中
        fgets();
        buff[strlen(buff)-1] = '\0';//清理結(jié)尾的\n

        //發(fā)送數(shù)據(jù)
        if(-1 == send(sockfd, buff, sizeof(buff), 0)){
            ERRLOG("send error");
        }
        
        //接收服務(wù)器的應(yīng)答信息
        if(-1 == (nbytes = recv(sockfd, buff, sizeof(buff), 0))){
            ERRLOG("recv error");
        }   
    }

六、測(cè)試結(jié)果

使用三個(gè)客戶端連接一個(gè)TCP并發(fā)服務(wù)器。

測(cè)試TCP并發(fā)服務(wù)器功能實(shí)現(xiàn)
【Linux網(wǎng)絡(luò)編程】TCP并發(fā)服務(wù)器的實(shí)現(xiàn)(IO多路復(fù)用select)
測(cè)試quit退出功能實(shí)現(xiàn)
【Linux網(wǎng)絡(luò)編程】TCP并發(fā)服務(wù)器的實(shí)現(xiàn)(IO多路復(fù)用select)
測(cè)試ctrl+c終止程序斷開來連接實(shí)現(xiàn)
【Linux網(wǎng)絡(luò)編程】TCP并發(fā)服務(wù)器的實(shí)現(xiàn)(IO多路復(fù)用select)
測(cè)試退出文件,清除客戶端的文件描述符,在新的客戶端連接時(shí),從最小的文件描述符開始實(shí)現(xiàn)。
【Linux網(wǎng)絡(luò)編程】TCP并發(fā)服務(wù)器的實(shí)現(xiàn)(IO多路復(fù)用select)
成功實(shí)現(xiàn)IO多路復(fù)用TCP并發(fā)服務(wù)器和客戶端。文章來源地址http://www.zghlxwxcb.cn/news/detail-435543.html

七、TCP并發(fā)服務(wù)器源代碼

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

#define ERRLOG(msg)                                        \
    do                                                     \
    {                                                      \
        printf("%s %s %d:", __FILE__, __func__, __LINE__); \
        perror(msg);                                       \
        exit(-1);                                          \
    } while (0)

#define N 128

int main(int argc, const char *argv[])
{
    //檢查入?yún)⒑侠硇?/span>
    if (argc != 3)
    {
        printf("Usage : %s <IP> <PORT>\n", argv[0]);
        return -1;
    }
    //創(chuàng)建套接字
    int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        ERRLOG("socket error");
    }
    //填寫服務(wù)器網(wǎng)絡(luò)信息結(jié)構(gòu)體
    struct sockaddr_in serviceaddr;
    memset(&serviceaddr, 0, sizeof(serviceaddr));
    serviceaddr.sin_family = AF_INET;
    serviceaddr.sin_addr.s_addr = inet_addr(argv[1]);
    serviceaddr.sin_port = htons(atoi(argv[2]));
    socklen_t serviceaddr_len = sizeof(serviceaddr);
    //將服務(wù)器網(wǎng)絡(luò)信息結(jié)構(gòu)體與套接字綁定
    if (bind(sockfd, (struct sockaddr *)&serviceaddr, serviceaddr_len) == -1)
    {
        ERRLOG("bind error");
    }
    //將套接字設(shè)置為被動(dòng)監(jiān)聽狀態(tài)
    if (listen(sockfd, 5) == -1)
    {
        ERRLOG("listen error");
    }
    char buf[N] = {0};
    int max_fd;
    int i;
    int ret;
    int acceptfd;
    int nbytes;
    //創(chuàng)建文件描述符集合母本和子本并進(jìn)行清空操作
    fd_set readfds;
    FD_ZERO(&readfds);
    fd_set readfds_msg;
    FD_ZERO(&readfds_msg);
    //將sockfd添加進(jìn)入集合內(nèi),并跟新最大文件描述符
    FD_SET(sockfd, &readfds);
    max_fd = max_fd > sockfd ? max_fd : sockfd;

    while (1)
    {
        //在每次循環(huán)前將子本重新賦值,因?yàn)閟elect會(huì)將沒有就緒的文件描述符在集合內(nèi)擦除
        readfds_msg = readfds;
        if ((ret = select(max_fd + 1, &readfds_msg, NULL, NULL, NULL)) == -1)
        {
            ERRLOG("select error");
        }
        else //說明有文件描述符就緒了
        {
            //遍歷文件描述符
            for (i = 3; i < max_fd + 1 && ret != 0; i++)
            {
                //判斷是哪個(gè)文件描述符就緒了
                if (FD_ISSET(i, &readfds_msg))
                {
                	ret--;
                    if (i == sockfd) //如果套接字就緒了則等待客戶端連接
                    {
                        if ((acceptfd = accept(sockfd, NULL, NULL)) == -1)
                        {
                            ERRLOG("accept error");
                        }
                        printf("客戶端[%d]連接到服務(wù)器..\n", acceptfd);
                        //如果有客戶端連接將產(chǎn)生的新的文件描述符添加到集合中,并更新最大文件描述符
                        FD_SET(acceptfd, &readfds);
                        max_fd = max_fd > acceptfd ? max_fd : acceptfd;
                    }
                    else //否則就是客戶端發(fā)來消息了
                    {
                        memset(buf, 0, N);
                        if ((nbytes = recv(i, buf, N, 0)) == -1)
                        {
                            ERRLOG("recv error");
                        }
                        else if (nbytes == 0)
                        {
                            printf("客戶端[%d]已斷開連接..\n", i);
                            close(i);            //關(guān)閉當(dāng)前客戶端的文件描述符
                            FD_CLR(i, &readfds); //將該客戶端的文件描述符在集合中刪除
                            continue;
                        }
                        if (strcmp(buf, "quit") == 0)
                        {
                            printf("客戶端[%d]已退出服務(wù)器..\n", i);
                            close(i);
                            FD_CLR(i, &readfds);
                            continue;
                        }
                        printf("客戶端[%d]發(fā)來消息[%s]..\n", i, buf);
                        strcat(buf, "--夜貓徐");      //組裝應(yīng)答
                        if (send(i, buf, N, 0) == -1) //發(fā)送給客戶端
                        {
                            ERRLOG("send error");
                        }
                    }
                }
            }
        }
    }
    close(sockfd);
    return 0;
}

八、客戶端源代碼

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

#define ERRLOG(msg) do{\
        printf("%s %s %d:", __FILE__, __func__, __LINE__);\
        perror(msg);\
        exit(-1);\
}while(0)

#define N 128

int main(int argc, const char *argv[]){
    //入?yún)⒑侠硇詸z查
    if(3 != argc){
        printf("Usage : %s <IP> <PORT>\n", argv[0]);
        return -1;
    }

    //1.創(chuàng)建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd){
        ERRLOG("socket error");
    }

    //2.填充服務(wù)器網(wǎng)絡(luò)信息結(jié)構(gòu)體
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t serveraddr_len = sizeof(serveraddr);

    char buff[128] = {0};
    int nbytes = 0;

    //3.嘗試與服務(wù)器建立連接
    if(-1 == connect(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len)){
        ERRLOG("connect error");
    }
    printf("與服務(wù)器建立連接成功..\n");
    while(1){
        memset(buff, 0, sizeof(buff));
        fgets(buff, N, stdin);
        buff[strlen(buff)-1] = '\0';//清理結(jié)尾的\n

        //發(fā)送數(shù)據(jù)
        if(-1 == send(sockfd, buff, sizeof(buff), 0)){
            ERRLOG("send error");
        }
        //接收服務(wù)器的應(yīng)答信息
        if(-1 == (nbytes = recv(sockfd, buff, sizeof(buff), 0))){
            ERRLOG("recv error");
        }
        if(0 == nbytes){
            break;
        }
        //輸出應(yīng)答信息
        printf("應(yīng)答為:[%s]\n", buff);
    }
    //關(guān)閉套接字
    close(sockfd);

    return 0;
}

到了這里,關(guān)于【Linux網(wǎng)絡(luò)編程】TCP并發(fā)服務(wù)器的實(shí)現(xiàn)(IO多路復(fù)用select)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

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

相關(guān)文章

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包