linux下通过socket实现聊天功能,代码如下:
头文件:Unp1.h
#include <sys/types.h> /* basic system data types */ #include <sys/socket.h> /* basic socket definitions */ #include <sys/time.h> /* timeval{} for select() */ #include <time.h> /* timespec{} for pselect() */ #include <netinet/in.h> /* sockaddr_in{} and other Internet defns */ #include <arpa/inet.h> /* inet(3) functions */ #include <errno.h> #include <fcntl.h> /* for nonblocking */ #include <netdb.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> /* for S_xxx file mode constants */ #include <sys/uio.h> /* for iovec{} and readv/writev */ #include <unistd.h> #include <sys/wait.h> #include <sys/un.h> #define MAXLINE 4096 #define LISTENQ 1024
/*chat.h*/ #ifndef __CHAT_H #define __CHAT_H #include "unp1.h" #define MAXUSERS 3 /* used to write number of n bytes to socket buffer*/ ssize_t writen(int fd,const void * vptr,size_t n); /*used to read number of n bytes from the socket buffer*/ ssize_t readn(int fd,void *vptr,size_t n); struct chat_struct{ int sock_fd; char user_id[20]; struct in_addr user_ipaddr; int next_char; int data_pos; struct client_cmd{ int cmd_type; char user_id[20]; }cmd; char buffer[MAXLINE]; int slot_status; }chater[MAXUSERS]; #define SLOT_FREED 0 #define SLOT_OCCUPIED 1 #define TELL_SOMEONE 1 #define CHAT_ALL 2 /*the two types of the user inputed*/ #define CHATER_LOGOUT 1 #define CHATER_ENTER_A_CMD 2 /*define the message notice to the users*/ char message[4][50]={"to many users in the system.\n", "can not find that user.\n'", "input your user_id:\n", "error command\n"}; /*it is used to select a free slot to the user,if there is not ,it will create one */ void get_free_slot(int sock_fd,char *user_name) { int j,flags; printf("enter get_free_slot.\n"); for(j=0;j<MAXUSERS;j++) { /*if the slot is free ,it will use it*/ if(chater[j].slot_status==SLOT_FREED) { chater[j].slot_status=SLOT_OCCUPIED; /*remmber the socket_fd in the slot*/ chater[j].sock_fd=sock_fd; /*save the username to supply to the tell*/ strcpy(chater[j].user_id,user_name); /*init the pointer whick point to the user_data_buffer*/ chater[j].next_char=0; /*get the identifer of the socket_fd*/ flags=fcntl(sock_fd,F_GETFL); if(flags==-1) { perror("get socket attributes error.\n"); exit(1); }else{ flags|=O_NONBLOCK; if(fcntl(sock_fd,F_SETFL,flags)==-1) { perror("change socket attributes failed!\n"); exit(1); } } break; } } if(j==MAXUSERS) { if(writen(sock_fd,message[0],strlen(message[0]))==-1) { perror("write to cilent failed!\n"); exit(1); } close(sock_fd); } } /* free_slot() release the slot to other users slot_index:send the chat user to use the slot */ void free_slot(int slot_index) { /*change the status of the slot*/ chater[slot_index].slot_status=SLOT_FREED; /*close the connection*/ close(chater[slot_index].sock_fd); } /* find_chater:return the slot_index,if the slot_index is -1,which means the the user is not in this system */ int find_chater(char *user_name) { int ret,i; for(i=0;i<MAXUSERS;i++) if(strcmp(chater[i].user_id,user_name)==0) { ret=i; break; } if(i==MAXUSERS) ret==-1; return ret; } /* tell_someone: handle the tell ,send message to the user */ void tell_someone(int slot_index) { int index,n; char *data_p; /*use the usename to find the slot*/ index=find_chater(chater[slot_index].cmd.user_id); if(index==-1) { /*if you do not find the user,so you send a message to the user */ if(writen(chater[slot_index].sock_fd,message[1],strlen(message[1]))==-1) { perror("write to the client failed!\n"); exit(1); } }else{ data_p=&(chater[slot_index].buffer[chater[slot_index].data_pos]); n=strlen(data_p); /*send message to the user of who send the tell*/ if(writen(chater[slot_index].sock_fd,data_p,n)==-1) { perror("write message to the user who send tell failed!\n"); exit(1); } } } /* chat_all:send the user_message to all the users who login in the system */ void chat_all(int slot_index) { int i,n; char *data_p; for(i=0;i<MAXUSERS;i++) { if(chater[i].slot_status!=SLOT_FREED&&slot_index!=i) { data_p=&(chater[slot_index].buffer[chater[i].data_pos]); n=strlen(data_p); if(writen(chater[i].sock_fd,data_p,n)==-1) { perror("writen failed!\n"); exit(1); } } } } /* handle_cmd:handle the message */ void handle_cmd(int slot_index) { char cmd[6]; char user_id[20],*pos; char detail[MAXLINE]; int ret,i; pos=&(chater[slot_index].buffer[0]); while(*pos==' '&&pos<&(chater[slot_index].buffer[MAXLINE])) pos++; if(*pos=='\n') { ret=writen(chater[slot_index].sock_fd,message[3],strlen(message[3])); if(ret<0&&errno!=EWOULDBLOCK) free_slot(slot_index); return; } /*read the command name*/ i=0; while(*pos!=' '&&pos<&(chater[slot_index].buffer[MAXLINE])) { cmd[i]=*pos++; i++; } cmd[i]='\0'; /*compare it is tell command*/ if(strcmp("tell",cmd)==0) { while(*pos!=' '&&pos<&(chater[slot_index].buffer[MAXLINE])) pos++; if(*pos=='\n') { ret=writen(chater[slot_index].sock_fd,message[3],strlen(message[3])); if(ret<0&&errno!=EWOULDBLOCK) free_slot(slot_index); return; } i=0; while(*pos!=' '&&pos<&(chater[slot_index].buffer[MAXLINE])) { user_id[i]=*pos++; i++; } user_id[i]='\0'; /*to find the real message*/ while(*pos=' '&&pos<&(chater[slot_index].buffer[MAXLINE])) { pos++; /*if the style is wrong ,so send the wrong message warning*/ if(*pos=='\n') { ret=writen(chater[slot_index].sock_fd,message[3],strlen(message[3])); if(ret<0&&errno!=EWOULDBLOCK) free_slot(slot_index); return; } /*to save the message place*/ chater[slot_index].data_pos=pos-chater[slot_index].buffer; /*save the revalent message in the stuct in client_cmd*/ strcpy(chater[slot_index].cmd.user_id,user_id); chater[slot_index].cmd.cmd_type=TELL_SOMEONE; } /*compare it is chat command or not*/ if(strcpy("chat",cmd)==0) { chater[slot_index].cmd.cmd_type=CHAT_ALL; chater[slot_index].data_pos=pos-chater[slot_index].buffer; } /*use the command_handle_function*/ if(chater[slot_index].cmd.cmd_type==TELL_SOMEONE) tell_someone(slot_index); if(chater[slot_index].cmd.cmd_type==CHAT_ALL) chat_all(slot_index); } } /* check_chater_status:check the status of the user which divide into three status, the frist:user send a message,the wait the server handle the message the second:user exit the syetem and wait the server clean the slot the third:the left */ int check_chater_status(int slot_index) { char *ptr; int n; ptr=&(chater[slot_index].buffer[chater[slot_index].next_char]); while((n=read(chater[slot_index].sock_fd,ptr,1))==1) { /*if the message you read is a '\n',which means the really message is arrived*/ if(*ptr=='\n') { /*set the next byte as '\0'*/ *(ptr+1)='\0'; /*init the place for the next data will readin */ chater[slot_index].next_char=0; /*return a command*/ return CHATER_ENTER_A_CMD; } /*if the client input the data fullfil the buffer,so ignore the next message*/ if(++chater[slot_index].next_char==MAXLINE) --chater[slot_index].next_char; else ++ptr; } /*if receive the end of the file,it means the user exit the system now*/ if(n==0) return CHATER_LOGOUT; if(n<0&&errno!=EWOULDBLOCK) free_slot(slot_index) ; return 0; } /* writen:send message to the buffer,then return the size of you send */ ssize_t writen(int fd,const void *vptr,size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr=vptr; nleft=n; while(nleft>0) { if(nwritten=write(fd,ptr,nleft)<=0) { if(errno==EWOULDBLOCK) nwritten=0; else return -1; } nleft-=nwritten; ptr+=nwritten; } return n; } /* readn:use to read data from the socket_buffer */ ssize_t readn(int fd,void *vptr,size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr=vptr; nleft=n; while(nleft>0) { if((nread=read(fd,ptr,nleft))<0) { if(errno==EWOULDBLOCK) nread=0; else return -1; }else if(nread==0) break; nleft-=nread; ptr+=nread; } return (n-nleft); } static ssize_t my_common_read(int fd,char *ptr) { static int read_cnt=0; static char *read_ptr; static char read_buf[MAXLINE]; if(read_cnt<=0) { again: if((read_cnt=read(fd,read_buf,sizeof(read_buf)))<0) { if(errno==EINTR) goto again; return -1; } else if(read_cnt==0) return 0; read_ptr=read_buf; } read_cnt--; *ptr=*read_ptr++; return 1; } /*read a line message from the socket_buffer*/ ssize_t commonreadline(int fd,void *vptr,size_t maxlen) { int n,rc; char c,*ptr; static times=0; ptr=vptr; for( n=0;n<maxlen;n++) { if((rc=my_common_read(fd,&c))==1) { *ptr++=c; if(c=='\n') { times++; if(times==1) break; } else times=0; }else if(rc==0) { if(n==1) return 0; else break; } else return -1; } *ptr=0; return n; } #endif
#include "unp1.h" #include "chat.h" #define SERVER_PORT 9000 int main(int argc,char * argv[]) { int listen_fd,conn_fd; socklen_t cli_len; struct sockaddr_in cli_addr,serv_addr; int ret,flags; int re_use_addr=1; char recv_buf[MAXLINE]; int i; char *ptr; for(i=0;i<MAXUSERS;i++) { chater[i].slot_status=SLOT_FREED; chater[i].sock_fd=-1; chater[i].cmd.cmd_type=-1; } listen_fd=socket(AF_INET,SOCK_STREAM,0); if(listen_fd==-1) { perror("listen socket failed!"); exit(1); } bzero(&serv_addr,sizeof(serv_addr)); serv_addr.sin_family=AF_INET; serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); serv_addr.sin_port=htons(SERVER_PORT); setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(void *)&re_use_addr,sizeof(int)); ret=bind(listen_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)); if(ret<0) { perror("bind socket failed!"); exit(1); } listen(listen_fd,LISTENQ); if((flags=fcntl(listen_fd,F_GETFL,0))<0) perror("F_GETFL error"); flags|=O_NONBLOCK; if(fcntl(listen_fd,F_SETFL,flags)<0) perror("F_SETFL error"); while(1) { cli_len=sizeof(cli_addr); conn_fd=accept(listen_fd,(struct sockaddr*)&cli_addr,&cli_len); if(conn_fd<0&&errno!=EWOULDBLOCK) { perror("accept socket failed!"); exit(1); }else if(conn_fd>0) { printf("the server create a new connection\n"); ret=writen(conn_fd,message[2],strlen(message[2])); if(ret<0&&errno==EINTR) { perror("write to the client failed!"); close(conn_fd); continue; } ret=commonreadline(conn_fd,recv_buf,MAXLINE); if(ret<0&&errno==EWOULDBLOCK) { printf("read from the client failed!"); close(conn_fd); continue; } ptr=recv_buf; while(*ptr!='\r') ptr++; *ptr='\0'; get_free_slot(conn_fd,recv_buf); } for(i=0;i<MAXUSERS;i++) { if(chater[i].slot_status==SLOT_OCCUPIED) { switch(check_chater_status(i)) { case CHATER_ENTER_A_CMD: handle_cmd(i); break; case CHATER_LOGOUT: free_slot(i); break; } } } } }