Linux下select函数实现的聊天服务器 佟强 http://blog.csdn.net/microtong
Windows下的图形客户端:
基于WSAAsyncSelect模型实现的聊天室图形客户端
http://blog.csdn.net/microtong/archive/2009/12/12/4990175.aspx
Window下的命令行客户端:
http://blog.csdn.net/microtong/archive/2009/12/12/4990138.aspx
消息缓冲区类MessageBuffer,接收线程将受到的消息放入缓冲区,发送线程从缓冲区中取出消息
MessageBuffer.h
//MessageBuffer.h #ifndef _MESSAGE_BUF_INCLUDE_ #define _MESSAGE_BUF_INCLUDE_ #include #define MESSAGE_COUNT 16 #define MESSAGE_LENGTH 2048 class MessageBuffer{ private: pthread_mutex_t mutex;//访问缓冲的互斥量 pthread_cond_t condition;//访问缓冲区的条件变量 //消息缓冲区,循环队列 char buf[MESSAGE_COUNT][MESSAGE_LENGTH]; int rear; //循环队列的队尾 int front; //循环队列的队首 public: bool toStop; //构造函数 MessageBuffer(); //析构函数 virtual ~MessageBuffer(); //将消息放入消息缓冲区,当缓冲区满时阻塞,toStop=true时返回-1 int PutMessage(const char *message); //从消息缓冲区中获得消息,当缓冲区空时阻塞,toStop=true时返回-1 int GetMessage(char *mbuf, int buflen); }; #endif
MessageBuffer.cpp
//MessageBuffer.cpp #include #include #include #include #include "MessageBuffer.h" MessageBuffer::MessageBuffer() { toStop = false; pthread_mutex_init(&mutex,NULL);//初始化互斥量 pthread_cond_init(&condition,NULL);//初始化条件变量 rear = 0; //队尾指针指向0 front = 0; //队首指针指向0 printf("A MessageBuffer intance created./n"); } MessageBuffer::~MessageBuffer(){ pthread_mutex_destroy(&mutex); pthread_cond_destroy(&condition); printf("A MessageBuffer instance destroyed./n"); } //将消息放入消息缓冲区 int MessageBuffer::PutMessage(const char *message){ struct timespec t; //等待互斥量 pthread_mutex_lock(&mutex); while(!toStop && (rear+1)%MESSAGE_COUNT==front){ t.tv_sec = time(NULL)+1; t.tv_nsec = 0; pthread_cond_timedwait(&condition,&mutex,&t); } if(toStop){ pthread_cond_broadcast(&condition); pthread_mutex_unlock(&mutex); return -1; } int messageLen = strlen(message); int copyLen = messageLen>=MESSAGE_LENGTH?MESSAGE_LENGTH-1:messageLen; memcpy(buf[rear],message,copyLen); buf[rear][copyLen]='/0'; rear = (rear+1)%MESSAGE_COUNT; pthread_cond_signal(&condition); pthread_mutex_unlock(&mutex); return 0; } //从消息缓冲区中获得消息 int MessageBuffer::GetMessage(char *mbuf, int buflen){ struct timespec t; pthread_mutex_lock(&mutex); while(!toStop && rear==front){ t.tv_sec = time(NULL)+1; t.tv_nsec = 0; pthread_cond_timedwait(&condition,&mutex,&t); } if(toStop){ pthread_cond_broadcast(&condition); pthread_mutex_unlock(&mutex); return -1; } int messageLen = strlen(buf[front]); int copyLen = messageLen>=buflen ? buflen-1 : messageLen; memcpy(mbuf,buf[front],copyLen); mbuf[copyLen]='/0'; front = (front+1)%MESSAGE_COUNT; pthread_cond_signal(&condition); pthread_mutex_unlock(&mutex); return 0; }
客户类Clients,用于维护套接字socket和套接字地址struct sockaddr_in之间的对应关系,并维护用户的姓名。
Clients.h
//Clients.h #ifndef _CLIENTS_INCLUDE_ #define _CLIENTS_INCLUDE_ #include #include #include #define NAME_LEN 50 #define MAX_CLIENT 30 typedef struct client_info{ int sock; struct sockaddr_in clientAddr; char name[NAME_LEN]; }CLIENT_INFO; class Clients{ private: pthread_mutex_t mutex; CLIENT_INFO client[MAX_CLIENT]; int clientCount; int IPtoString(unsigned long ip, char *buf, int buflen); int Search(int sock); public: Clients();//构造函数 virtual ~Clients();//析构函数 int GetClientCount(); bool PutClient(int sock,const struct sockaddr_in &clientAddr); void RemoveClient(int sock); bool GetAddrBySocket(int sock,struct sockaddr_in *addr); bool PutName(int sock,const char *name, int namelen); bool GetName(int sock, char *name, int namelen); int GetAllSocket(int* sockArray, int arrayLen ); }; #endif
Clients.cpp
//Clients.cpp #include #include #include #include "Clients.h" Clients::Clients() { pthread_mutex_init(&mutex, NULL); clientCount = 0; printf("Clients created./n"); } Clients::~Clients() { pthread_mutex_destroy(&mutex); printf("Clients destroyed./n"); } int Clients::Search(int sock){ int index = -1; for(int i=0; i=NAME_LEN ? NAME_LEN-1:namelen; memcpy(client[index].name,name,copyLen); client[index].name[copyLen]='/0'; } pthread_mutex_unlock(&mutex); return index!=-1; } bool Clients::GetName(int sock, char *name, int namelen) { pthread_mutex_lock(&mutex); int index = Search(sock); if(index!=-1){ int msgLen = strlen(client[index].name); int copyLen = (msgLenclientCount ? clientCount : arrayLen; for(int i=0; i
聊天室服务器主程序Server.cpp
/*server.c*/ #include #include #include #include #include #include #include #include #include #include #include #include "MessageBuffer.h" #include "Clients.h" using namespace std; #define SERVER_PORT 8000 #define BUFFER_SIZE 4096 #ifndef MAX_CLIENT #define MAX_CLIENT 30 #endif #ifndef NAME_LEN #define NAME_LEN 50 #endif MessageBuffer messageBuffer; Clients clients; void* ListenThread(void*); void* RecvThread(void*); void* SendThread(void*); void ProcessMessage(int sock,char buf[],int bufsize,int bytes); bool toStop=false; int main(int argc,char* argv[]) { if(argc!=2){ printf("Usage: %s PortNumber/n",argv[0]); return -1; } unsigned short port; if((port = atoi(argv[1]))==0){ printf("incorrect port number./n"); return -1; } int s; struct sockaddr_in serverAddr; s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if(s==-1){ fprintf(stderr,"create socket failed./n"); return -1; } bzero(&serverAddr,sizeof(struct sockaddr_in)); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(port); serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(s,(struct sockaddr*)&serverAddr,sizeof(serverAddr))==-1){ fprintf(stderr,"bind socket to port %d failed./n",port); return -1; } if(listen(s,SOMAXCONN)==-1){ fprintf(stderr,"listen failed./n"); return -1; } printf("Server is listening on "); char hostname[255]; if(gethostname(hostname,sizeof(hostname))){ printf("gethostname() failed./n"); return -1; } struct hostent* pHost = gethostbyname(hostname); if(pHost){ for(int i=0; pHost->h_addr_list[i]; i++){ printf("%s ",inet_ntoa(*(in_addr*)pHost->h_addr_list[i])); } } printf("/nport: %d/n",port); pthread_t tListenId; if(pthread_create(&tListenId,NULL,ListenThread,&s)){ printf("failed to create listen thread./n"); return -1; } pthread_t tRecvId; if(pthread_create(&tRecvId,NULL,RecvThread,NULL)){ printf("failed to create recv thread./n"); return -1; } pthread_t tSendId; if(pthread_create(&tSendId,NULL,SendThread,NULL)){ printf("failed to create send thread./n"); return -1; } while(getchar()!='q'); toStop = true; messageBuffer.toStop = true; pthread_join(tListenId,NULL); pthread_join(tRecvId,NULL); pthread_join(tSendId,NULL); close(s); int sock[MAX_CLIENT]; int count = clients.GetAllSocket(sock,MAX_CLIENT); for(int i=0;imaxfd){ maxfd = sock[i]; } } printf("/n"); timeout.tv_sec = 2; timeout.tv_usec = 0; int ret = select(maxfd+1,&readSet,NULL,NULL,&timeout); if(toStop){ printf("RecvThread: exit./n"); return NULL; } if(ret==-1){ printf("RecvThread: select() failed!/n"); }else if(ret==0){ printf("RecvThread: select() time out./n"); }else{ for(int i=0; imaxfd){ maxfd = sock[i]; } } timeout.tv_sec = 2; timeout.tv_usec = 0; ret = select(maxfd+1,NULL,&writeSet,NULL,&timeout); if(toStop){ printf("SendThread: exit./n"); return NULL; } if(ret==-1){ printf("SendThread: select() failed!/n"); }else if(ret==0){ printf("SendThread: select() time out./n"); }else{ for(int i=0;i=NAME_LEN){ copyLen = NAME_LEN-1; }else{ copyLen = nameLen; } memcpy(newname,buf+5,copyLen); newname[copyLen]='/0'; clients.GetName(sock,name,sizeof(name)); sprintf(bufWithName,"%s change name to %s",name,newname); clients.PutName(sock,newname,strlen(newname)); messageBuffer.PutMessage(bufWithName); }else{ clients.GetName(sock,name,sizeof(name)); sprintf(bufWithName,"%s: %s",name,buf); messageBuffer.PutMessage(bufWithName); } } }
编译脚本文件compile
g++ -c MessageBuffer.cpp
g++ -c Clients.cpp
g++ -c Server.cpp
g++ -lpthread -o server MessageBuffer.o Clients.o Server.o
chmod +x compile
./compile 就可以编译并链接
运行服务器
./server 8000
注意Linux下的防火墙iptables服务是否已经启动,如果启动了,需要在/etc/sysconfig/iptables中加入例外端口8000,并重启启动防火墙
/etc/init.d/iptables restart
佟强的高级网络编程课件 http://www.oakcms.cn/network