고급 입출력 재사용 애플리케이션: 채팅방 프로그램

간단한 채팅방 프로그램: 클라이언트는 표준 입력에서 데이터를 입력한 후 서버에 전송하고 서버는 사용자가 보낸 데이터를 다른 사용자에게 전송한다.이곳은 IO 복용 폴 기술을 채택한다.클라이언트가 splice 제로 복사본을 채택했습니다.서비스 측은 공간 교환 시간을 채택했다(초대형 사용자 데이터 그룹을 분배한 다음에 사용자가 연결된 파일 설명자를 통해 사용자 데이터를 인덱스할 수 있다)
클라이언트 프로그램:
#define _GNU_SOURCE 1//    POLLRDHUP  
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<poll.h>
#include<fcntl.h>
#include<iostream>
#define BUFFER_SIZE 64
using namespace std;
int main(int argc,char* argv[]){
    if(argc<=2){
        cout<<"argc<=2"<<endl;
        return 1;
    }
    const char* ip=argv[1];//     
    int port=atoi(argv[2]);
    struct sockaddr_in address;
    bzero(&address,sizeof(address));
    address.sin_family=AF_INET;
    inet_pton(AF_INET,ip,&address.sin_addr);
    address.sin_port=htons(port);
    int sockfd=socket(PF_INET,SOCK_STREAM,0);
    assert(sockfd>=0);
    if(connect(sockfd,(struct sockaddr*)&address,sizeof(address))<0){
        cout<<"connect error"<<endl;
        close(sockfd);
        return 1;
    }
    pollfd fds[2];//pollfd     
    fds[0].fd=0;//fds[0]     
    fds[0].events=POLLIN;//      
    fds[0].revents=0;
    fds[1].fd=sockfd;//fds[1] socket   
    fds[1].events=POLLIN|POLLRDHUP;//         
    fds[1].revents=0;
    char read_buf[BUFFER_SIZE];
    int pipefd[2];
    int ret=pipe(pipefd);//      ,splice               (     )
    assert(ret!=-1);
    while(1){
        ret=poll(fds,2,-1);//           
        if(ret<0){
            cout<<"poll error"<<endl;
            break;
        }
        if(fds[1].revents&POLLRDHUP){//  socket                  
            cout<<"server close the connection"<<endl;
            break;
        }
        else if(fds[1].revents&POLLIN){//sokect       
            memset(read_buf,'\0',BUFFER_SIZE);
            recv(fds[1].fd,read_buf,BUFFER_SIZE-1,0);//           (                  )
            cout<<read_buf<<endl;
        }
        if(fds[0].revents&POLLIN){//           (                 )
            ret=splice(0,NULL,pipefd[1],NULL,32768,SPLICE_F_MORE|SPLICE_F_MOVE);//                 
            ret=splice(pipefd[0],NULL,sockfd,NULL,32768,SPLICE_F_MORE|SPLICE_F_MOVE);//            socket   
        }
    }
    close(sockfd);
    return 0;
}

서버 프로그램:
#define _GNU_SOURCE 1//  POLLRDHUP  
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<fcntl.h>
#include<stdlib.h>
#include<poll.h>
#include<iostream>
#define USER_LIMIT 5//     ,         poll  
#define BUFFER_SIZE 64//     
#define FD_LIMIT 65535//       
using namespace std;
struct client_data{//    :   socket  、           、         。             
    sockaddr_in address;
    char* write_buf;
    char buf[BUFFER_SIZE];
};
int setnonblocking(int fd){//           
    int old_option=fcntl(fd,F_GETFL);
    int new_option=old_option|O_NONBLOCK;
    fcntl(fd,F_SETFL,new_option);
    return old_option;
}
int main(int argc,char* argv[]){
    if(argc<=2){
        cout<<"argc<=2"<<endl;
        return 1;
    }
    const char* ip=argv[1];
    int port=atoi(argv[2]);
    int ret=0;
    struct sockaddr_in address;//     
    bzero(&address,sizeof(address));
    address.sin_family=AF_INET;
    inet_pton(AF_INET,ip,&address.sin_addr);
    address.sin_port=htons(port);
    int listenfd=socket(PF_INET,SOCK_STREAM,0);
    assert(listenfd>=0);
    ret=bind(listenfd,(struct sockaddr*)&address,sizeof(address));
    assert(ret!=-1);
    ret=listen(listenfd,5);
    assert(listenfd!=-1);
    client_data* users=new client_data[FD_LIMIT];//             ,                                  
    pollfd fds[USER_LIMIT+1];//pollfd     ,        poll  
    int user_count=0;
    for(int i=0;i<=USER_LIMIT;i++){//   poll  
        fds[i].fd=-1;//       
        fds[i].events=0;
    }
    fds[0].fd=listenfd;
    fds[0].events=POLLIN|POLLERR;//             
    fds[0].revents=0;
    while(1){
        ret=poll(fds,user_count+1,-1);//           
        if(ret<0){
            cout<<"poll error"<<endl;
            break;
        }
        for(int i=0;i<user_count+1;i++){//  poll   ,           (+1             )
            if((fds[i].fd==listenfd)&&(fds[i].revents&POLLIN)){//                     
                struct sockaddr_in client_address;
                socklen_t client_addrlength=sizeof(client_address);
                int connfd=accept(listenfd,(struct sockaddr*)&client_address,&client_addrlength);//      
                if(connfd<0){
                    cout<<"accept error "<<strerror(errno)<<endl;
                    continue;
                }
                if(user_count>=USER_LIMIT){//              ,      
                    const char* info="too many users,you can't connect
"; cout<<info<<endl; send(connfd,info,strlen(info),0); close(connfd); continue; } user_count++; users[connfd].address=client_address;// setnonblocking(connfd); fds[user_count].fd=connfd; fds[user_count].events=POLLIN|POLLRDHUP|POLLERR; fds[user_count].revents=0; cout<<"a new user come id:"<<user_count<<endl; } else if(fds[i].revents&POLLERR){// cout<<"poll error in:"<<fds[i].fd<<endl; char errors[100]; memset(errors,'\0',100); socklen_t length=sizeof(errors); if(getsockopt(fds[i].fd,SOL_SOCKET,SO_ERROR,&errors,&length)<0){ cout<<"get socket option error"<<endl; } continue; } else if(fds[i].revents&POLLRDHUP){// users[fds[i].fd]=users[fds[user_count].fd]; close(fds[i].fd); fds[i]=fds[user_count]; i--; user_count--; cout<<"a user leave"<<endl; } else if(fds[i].revents&POLLIN){// , int connfd=fds[i].fd; memset(users[connfd].buf,'\0',BUFFER_SIZE-1); ret=recv(connfd,users[connfd].buf,BUFFER_SIZE-1,0); cout<<"get data:"<<users[connfd].buf<<" from user:"<<connfd<<" bytes:"<<ret<<endl; if(ret<0){ if(errno!=EAGAIN){// EAGAIN , , EAGAIN close(connfd);// users[fds[i].fd]=users[fds[user_count].fd]; fds[i]=fds[user_count]; i--; user_count--; } } else if(ret==0){}// ###1### POLLIN else{ for(int j=1;j<=user_count;j++){// POLLOUT if(fds[j].fd==connfd){ continue; } fds[j].events|=~POLLIN; fds[j].events|=POLLOUT; users[fds[j].fd].write_buf=users[connfd].buf;// } } } else if(fds[i].revents&POLLOUT){// POLLOUT int connfd=fds[i].fd; if(!users[connfd].write_buf){ continue; } ret=send(connfd,users[connfd].write_buf,strlen(users[connfd].write_buf),0); users[connfd].write_buf=NULL;// fds[i].events|=~POLLOUT; fds[i].events|=POLLIN;//###1### } } } delete[] users; close(listenfd); return 0; }// A , , , A , POLLOUT

좋은 웹페이지 즐겨찾기