Webserver(4.5)复用

目录

端口复用

单边的close,会有一个等待的状态,这个时候有1分钟的时间,端口还开启着。这个时候再打开服务器,可能会有端口依旧绑定的情况

端口复用来重启这个端口,就能重新绑定了

cpp 复制代码
int optval=1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));
cpp 复制代码
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>


int main(){
    //1.创建socket(用于监听的套接字)
    int lfd=socket(AF_INET,SOCK_STREAM,0);
    if(lfd==-1){
        perror("socket");
        exit(0);
    }
    int optval=1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));
    //2.绑定
    struct sockaddr_in saddr;
    saddr.sin_family=AF_INET;
    //indet_pton(AF_INET,"192.168.227.129",saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr=INADDR_ANY;
    saddr.sin_port=htons(9999);
    int ret=bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
    if(ret==-1){
        perror("bind");
        exit(0);
    }

    //3.监听
    ret=listen(lfd,8);
    if(ret==-1){
        perror("listen");
        exit(0);
    }

    //4.接收客户端的连接
    struct sockaddr_in clientaddr;
    socklen_t len=sizeof(clientaddr);
    int cfd=accept(lfd,(struct sockaddr *)&clientaddr,&len);
    if(cfd==-1){
        perror("accept");
        exit(0);
    }

    //输出客户端的信息
    char clientIP[16];
    inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,clientIP,sizeof(clientIP));
    unsigned short clientPort =ntohs(clientaddr.sin_port);
    printf("client ip is %s,port is %d\n",clientIP,clientPort);

    //5.通信
    char recvBuf[1024]={0};
    //获取客户端的数据
    while(1){
        
        int num=recv(cfd,recvBuf,sizeof(recvBuf),0);
        if(num==-1){
            perror("recv");
            exit(-1);
        }else if(num>0){
            printf("recv client data:%s\n",recvBuf);
        }else if(num==0){
            //表示客户端断开连接
            printf("client closed...");
            break;
        }

        //小写转大写
        for(int i=0;i<len;++i){
            recvBuf[i]=toupper(recvBuf[i]);
        }
        printf("after buf=%s\n",recvBuf);

        //大写字符串发给客户端
        ret=send(cfd,recvBuf,strlen(recvBuf)+1,0);
        if(ret==-1){
            perror("send");
            return -1;
        }

    }
    

    //关闭文件描述符
    close(cfd);
    close(lfd);

    return 0;
}
cpp 复制代码
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>

int main(){

    //1.创建套接字
    int fd=socket(AF_INET,SOCK_STREAM,0);
    if(fd==-1){
        perror("socket");
        exit(-1);
    }

    //2.连接服务器端
    struct sockaddr_in serveraddr;
    serveraddr.sin_family=AF_INET;
    inet_pton(AF_INET,"192.168.227.129",&serveraddr.sin_addr.s_addr);
    serveraddr.sin_port=htons(9999);
    int ret=connect(fd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
    if(ret==-1){
        perror("connect");
        exit(-1);
    }

    char recvBuf[1024]={0};
    //3.通信
    while(1){
        char sendBuf[1024]={0};
        fgets(sendBuf,sizeof(sendBuf),stdin);
        //给服务器发送数据
        write(fd,sendBuf,strlen(sendBuf)+1);
        
        
        int len=read(fd,sendBuf,sizeof(sendBuf));
        if(len==-1){
            perror("read");
            exit(-1);
        }else if(len>0){
            printf("recv server data:%s\n",sendBuf);
        }else if(len==0){
            //表示客户端断开连接
            printf("server closed...");
        }
    }
   

    //关闭连接
    close(fd);



    return 0;
}

I/O多路复用

select

构造一个关于文件描述符的列表

监听文件是否发送数据,告诉进程有哪些描述符需要进行I/O操作

两个客户端也可以

并没有用多进程或者多线程,只使用了select也能完成多客户端的连接

client.c

cpp 复制代码
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>

int main(){

    //1.创建套接字
    int fd=socket(AF_INET,SOCK_STREAM,0);
    if(fd==-1){
        perror("socket");
        exit(-1);
    }

    //2.连接服务器端
    struct sockaddr_in serveraddr;
    serveraddr.sin_family=AF_INET;
    inet_pton(AF_INET,"127.0.0.1",&serveraddr.sin_addr.s_addr);
    serveraddr.sin_port=htons(9999);
    int ret=connect(fd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
    if(ret==-1){
        perror("connect");
        exit(-1);
    }


    //3.通信
    int num=0;
    while(1){
        char sendBuf[1024]={0};
        sprintf(sendBuf,"send data %d",num++);
        sleep(1);
        //给服务器发送数据
        write(fd,sendBuf,strlen(sendBuf)+1);
        
        
        int len=read(fd,sendBuf,sizeof(sendBuf));
        if(len==-1){
            perror("read");
            exit(-1);
        }else if(len>0){
            printf("recv server data:%s\n",sendBuf);
        }else if(len==0){
            //表示客户端断开连接
            printf("server closed...");
        }
    }
   

    //关闭连接
    close(fd);



    return 0;
}

select.c

cpp 复制代码
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/time.h>
#include<sys/types.h>
#include<sys/select.h>

int main(){

    //创建socket
    int lfd=socket(PF_INET,SOCK_STREAM,0);
    struct sockaddr_in saddr;
    saddr.sin_port=htons(9999);
    saddr.sin_family=AF_INET;
    saddr.sin_addr.s_addr=INADDR_ANY;

    //绑定
    bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));

    //监听
    listen(lfd,8);

    //创建一个fd_set的集合,存放的是需要检测的文件描述符
    fd_set rdset,tmp;
    FD_ZERO(&rdset);
    FD_SET(lfd,&rdset);
    int maxfd=lfd;

    while(1){
        tmp=rdset;
        //调用select系统函数,让内核帮检测哪些文件描述符有数据
        int ret=select(maxfd+1,&tmp,NULL,NULL,NULL);
        if(ret==-1){
            perror("select");
            exit(-1);
        }else if(ret==0){
            continue;
        }else if(ret>0){
            //说明检测到了有文件描述符的对应的缓冲区的数据发生了改变
            if(FD_ISSET(lfd,&tmp)){
                //表示有新的客户端连接进来了
                struct sockaddr_in cliaddr;
                int len=sizeof(cliaddr);
                int cfd=accept(lfd,(struct sockaddr *)&cliaddr,&len);

                //将新的文件描述符加入到集合中
                FD_SET(cfd,&rdset);

                //更新最大的文件描述符
                maxfd=maxfd>cfd? maxfd:cfd;

            }

            for(int i=lfd+1;i<=maxfd;i++){
                if(FD_ISSET(i,&rdset)){
                    //说明这个文件描述符对应的客户端发来了数据
                    char buf[1024]={0};
                    int len=read(i,buf,sizeof(buf));
                    if(len==-1){
                        perror("read");
                        exit(-1);
                    }else if(len==0){
                        printf("client closed..\n");
                        close(i);
                        FD_CLR(i,&rdset);
                    }else if(len>0){
                        printf("read buf:%s\n",buf);
                        write(i,buf,strlen(buf)+1);
                    }
                }
            }

        }
        
    }
    close(lfd);

    return 0;
}
相关推荐
爱喝水的小周3 小时前
实验八 Vue‑Router 路由插件实践
前端·javascript
江屿风3 小时前
【Linux系统】【Linux 进程程序替换机制解析及自定义 Shell 核心逻辑实现 】流食般投喂
linux·运维·服务器·开发语言·笔记
ℋᙚᵐⁱᒻᵉ鲸落3 小时前
ETypeError: nameRef.value?.focus is not a functio. 设置el-input自动聚焦时报错
前端·javascript·vue.js·elementui
SEO_juper3 小时前
2026年用Python检查多语言站点hreflang:自动揪出“回链缺失/标错语言“等致命错误(附完整代码)
开发语言·前端·seo·独立站·谷歌优化
三少爷的鞋3 小时前
Kotlin 协程闯关:看代码,猜结果
android
2501_932750265 小时前
Android 跑马灯:从一行 XML 到自定义控件
android
aramae12 小时前
模拟实现strcmp()(C语言)
c语言·开发语言·后端
泡海椒13 小时前
PDF 表格样式优化:jquick-pdf 边框、圆角、背景色
java·开发语言·pdf
半个落月13 小时前
LangChain.js Agent Memory 实战(下):用 Milvus 构建可检索的长期记忆
javascript·langchain
半个落月13 小时前
LangChain.js Agent Memory 实战(上):从内存对话、文件持久化到截断与摘要
javascript·langchain