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;
}
相关推荐
Eastsea.Chen20 分钟前
MTK Android12 user版本MtkLogger
android·framework
Ajiang282473530421 分钟前
对于C++中stack和queue的认识以及priority_queue的模拟实现
开发语言·c++
ggdpzhk21 分钟前
VUE:基于MVVN的前端js框架
前端·javascript·vue.js
幽兰的天空26 分钟前
Python 中的模式匹配:深入了解 match 语句
开发语言·python
小曲曲1 小时前
接口上传视频和oss直传视频到阿里云组件
javascript·阿里云·音视频
学不会•2 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNTS3 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
Theodore_10223 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
活宝小娜5 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点5 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript