고급 입출력 재사용 애플리케이션: 채팅방 프로그램
8769 단어 고급 입출력 재사용 애플리케이션 채팅방 프로그램
클라이언트 프로그램:
#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