一步一步创建聊天程序2-利用epoll来创建简单的聊天室
如图,这个是看视频时,最后的作业,除了客户端未使用select实现外,其它的要求都有简单实现。
服务端代码如下:
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <sys/epoll.h> #include <netinet/in.h> #include <unistd.h> #include<netinet/tcp.h> #define MAX_LISTEN 10 #define EPOLL_SIZE 100 struct message { int target_id; char buf[100]; }; struct user_password { char username[100]; char password[100]; }; //设置保活参数 keepalive_time保活时间,keepalive_intvl保活间隔,keepalive_probes保活探测数。 int set_keepalive(int sockfd,int keepalive_time, int keepalive_intvl, int keepalive_probes) { int optval; socklen_t optlen= sizeof(optval); optval=1; if(-1==setsockopt(sockfd,SOL_SOCKET, SO_KEEPALIVE, &optval, optlen)) { perror("setsockopt failture.\n"); return -1; } optval=keepalive_probes; if(-1==setsockopt(sockfd,SOL_TCP,TCP_KEEPCNT, &optval,optlen)) { perror("setsockopt failture.\n"); return -1; } optval=keepalive_intvl; if(-1==setsockopt(sockfd,SOL_TCP,TCP_KEEPINTVL, &optval,optlen)) { perror("setsockopt failture.\n"); return -1; } optval=keepalive_time; if(-1==setsockopt(sockfd,SOL_TCP,TCP_KEEPIDLE, &optval, optlen)) { perror("setsockopt failture\n"); return -1; } return 0; } void set_user_password(struct user_password users[100]) { int fd=fopen("./password.txt","r"); if(fd==NULL) { perror("open password failed\n"); return; } char buf[100]; int count=0; int i=0; while(feof(fd)==0) { fscanf(fd,"%s",buf); if(count%2==0) { strcpy(users[i].username,buf); count++; } else { strcpy(users[i++].password,buf); count++; } } fclose(fd); } int main(int argc,char * argv[]) { //读取设定的用户名和密码 struct user_password users[100]; set_user_password(users); for(int i=0;i<2;i++) { printf("user: %s,\t password: %s\n",users[i].username,users[i].password); } struct sockaddr_in server_ip,customer_ip; int err,sd; sd=socket(AF_INET,SOCK_STREAM,0); if(sd==-1) { perror("socket failed\n"); close(sd); return -1; } server_ip.sin_family=AF_INET; server_ip.sin_port=htons(5678); server_ip.sin_addr.s_addr=htonl(INADDR_ANY); memset(server_ip.sin_zero,0,8); err=bind(sd,(struct sockaddr *)(&server_ip),sizeof(struct sockaddr)); if(err==-1) { perror("bind failed\n"); close(sd); return -1; } err=listen(sd,MAX_LISTEN); if(err==-1) { perror("listen failed\n"); close(sd); return -1; } int epfd=epoll_create(EPOLL_SIZE); if(epfd<0) { perror("epoll create failed\n"); return -1; } printf("epoll created, epollfd=%d\n",epfd); struct epoll_event events[EPOLL_SIZE]; struct epoll_event event; event.data.fd=sd; event.events=EPOLLIN; err=epoll_ctl(epfd,EPOLL_CTL_ADD,sd,&event); if(err==-1) { perror("epoll add failed\n"); return -1; } while(1) { int epoll_events_count=epoll_wait(epfd,events,EPOLL_SIZE,-1); if(epoll_events_count<0) { perror("epoll wait failed\n"); break; } printf("epoll_events_count= %d\n",epoll_events_count); for(int i=0;i<epoll_events_count;i++) { int sockfd=events[i].data.fd; if(sockfd==sd) { int length=sizeof(struct sockaddr); int client_fd=accept(sockfd,(struct sockaddr *)(&customer_ip),&length); //用户名和密码检验 int flag=0; char buf[100]="please input username and password "; struct user_password login_user; send(client_fd,buf,100,0); recv(client_fd,&login_user,sizeof(struct user_password),0); memset(buf,0,100); for(int i=0;i<100;i++) { if((strcmp(users[i].username,login_user.username)==0)&&(strcmp(users[i].password,login_user.password)==0)) { flag=1; break; } } if(flag) { err=set_keepalive(sockfd,120,20,3); if(err!=0) { perror("set keep alive failed.\n"); continue; } printf("USER %d online\n",client_fd); event.data.fd=client_fd; event.events=EPOLLIN; err=epoll_ctl(epfd,EPOLL_CTL_ADD,client_fd,&event); if(err==-1) { perror("epoll add failed\n"); return -1; } } else { memset(buf,0,100); strcpy(buf,"login failed\n"); send(client_fd,buf,100,0); } } else { struct message recv_message; int bytes=recv(sockfd,&recv_message,sizeof(struct message),0); if(bytes<0) { perror("recv failed\n"); return -1; } else if(bytes==0) { err=epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,events); if(err==-1) { perror("epoll del failed\n"); return -1; } printf("USER %d offline\n",sockfd); close(sockfd); } else { printf("recv message from cliend %d, buf: %s\n",sockfd,recv_message.buf); if(recv_message.target_id!=-1)//请求向客户端发送信息 { err=send(recv_message.target_id,recv_message.buf,100,0); if(err==-1) { perror("send failed\n"); } } else if(strcmp(recv_message.buf,"quit")==0)//用户输入quit,客户端推出。 { printf("USER %d offline\n",sockfd); send(sockfd,recv_message.buf,100,0); close(sockfd); } else { char cmd[100]; strcat(cmd,recv_message.buf); strcat(cmd," >> test.txt"); int fd=fopen("./test.txt","w"); if(fd==NULL) { perror("clear file failed\n"); continue; } close(fd); printf("exec cmd: %s\n",cmd); system(cmd); memset(cmd,0,100); //将结果回送客户端 fd=fopen("./test.txt","r"); if(fd==NULL) { perror("open file failed\n"); continue; } char buf[100]; while(feof(fd)==0) { int fread_ret=fread(buf,sizeof(char),sizeof(buf),fd); send(sockfd,buf,100,0); memset(buf,0,128); } fclose(fd); } } } } } return 0; }
客户端代码还是之前的版本,没有使用select实现:
#include <stdio.h> #include <pthread.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> struct message { int target_id; char buf[100]; }; struct user_password { char username[100]; char password[100]; }; int sd; struct message send_message; void * read_message(void * argv) { while(1) { //读服务器发来的消息 char revBuf[100]; read(sd,revBuf,100); printf("recevice from server: %s",revBuf); } } void * write_message(void * argv) { while(1) { printf("input message: \n"); memset(send_message.buf,0,128); send_message.target_id=-1; scanf("%d %s",&send_message.target_id,send_message.buf); write(sd,&send_message,sizeof(send_message)); sleep(3); } } int main() { struct sockaddr_in server_ip,customer_ip; int err; sd=socket(AF_INET,SOCK_STREAM,0); if(sd==-1) { printf("socket failed\n"); close(sd); return -1; } //server_ip初始化 server_ip.sin_family=AF_INET; server_ip.sin_port=htons(5678); server_ip.sin_addr.s_addr=htonl(INADDR_ANY); //err=inet_aton("115.157.201.179",&server_ip.sin_addr.s_addr); memset(server_ip.sin_zero,0,8); err=connect(sd,(struct sockaddr *)(&server_ip),sizeof(server_ip)); if(err==-1) { printf("connect failed\n"); close(sd); return -1; } pid_t pid=fork(); if(pid==0) { while(1) { //读服务器发来的消息 //printf("read message: \n"); char revBuf[100]; recv(sd,revBuf,100,0); //read(sd,revBuf,100); if(strcmp(revBuf,"quit")==0) { return 0; } printf("recevice from server: %s\n",revBuf); } } int flag=0; while(1) { printf("input message: \n"); if(flag++==0) { struct user_password login_user; scanf("%s %s",login_user.username,login_user.password); err=send(sd,&login_user,sizeof(struct user_password),0); if(err==-1) { printf("send failed\n"); } continue; } memset(send_message.buf,0,128); send_message.target_id=-1; scanf("%d %s",&send_message.target_id,send_message.buf); //if(send_message.target_id!=-1&&(strcmp(send_message.buf,"")!=0)) //{ //err=send(sd,send_message.buf,100,0); err=send(sd,&send_message,sizeof(send_message),0); if(err==-1) { printf("send failed\n"); } //write(sd,&send_message,sizeof(send_message)); //} if(strcmp(send_message.buf,"quit")==0) { printf("USER offline\n"); close(sd); return 0; } send_message.target_id=-1; memset(send_message.buf,0,sizeof(send_message.buf)); sleep(3); } close(sd); return 0; }