C++实现一款简单完整的聊天室服务器+客户端

清泛编译
目录:
Linux下select函数实现的聊天服务器
基于WSAAsyncSelect模型实现的聊天室图形客户端
聊天室Windows控制台客户端

Linux下select函数实现的聊天服务器

消息缓冲区类MessageBuffer,接收线程将受到的消息放入缓冲区,发送线程从缓冲区中取出消息
MessageBuffer.h
//MessageBuffer.h
#ifndef _MESSAGE_BUF_INCLUDE_
#define _MESSAGE_BUF_INCLUDE_
#include <pthread.h>
#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 <stdio.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#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 <sys/types.h>
#include <netinet/in.h>
#include <pthread.h>
#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 <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#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<clientCount; i++) {
     if(client[i].sock==sock){
        index = i;
        break;
     }
  }
  return index;
}
int Clients::IPtoString(unsigned long ip,char *buf,int buflen){
    unsigned char *p = (unsigned char*)&ip;
    if(buflen<16){
       return -1;
    }
    sprintf(buf,"%u.%u.%u.%u",*p,*(p+1),*(p+2),*(p+3));
    return strlen(buf);
}
int Clients::GetClientCount(){
   return clientCount;
}
  
bool Clients::PutClient(int sock,const struct sockaddr_in &clientAddr) {
  if(clientCount==MAX_CLIENT){
    return false;
  }
  pthread_mutex_lock(&mutex);
  client[clientCount].sock = sock;
  client[clientCount].clientAddr = clientAddr;
  int buflen = sizeof(client[clientCount].name);
  int pos = IPtoString(clientAddr.sin_addr.s_addr,client[clientCount].name,buflen);  
  sprintf(&client[clientCount].name[pos],":%d",ntohs(clientAddr.sin_port));
  
  clientCount++;
  pthread_mutex_unlock(&mutex);
  return true;
}
void Clients::RemoveClient(int sock){
  pthread_mutex_lock(&mutex);
  int index = Search(sock);
  if(index!=-1){
    for(int i=index; i<clientCount-1; i++){
	client[i] = client[i+1];
    }
    clientCount--;
  }  
  pthread_mutex_unlock(&mutex);
}
  
bool Clients::GetAddrBySocket(int sock,struct sockaddr_in *addr){
  pthread_mutex_lock(&mutex);
  int index = Search(sock);
  if(index!=-1){
    memcpy(addr,&client[index].clientAddr,sizeof(struct sockaddr_in));
  }
  pthread_mutex_unlock(&mutex);
  return index!=-1;
}
bool Clients::PutName(int sock,const char *name,int namelen) {
  pthread_mutex_lock(&mutex);
  int index = Search(sock);
  if(index!=-1){
    int copyLen = namelen>=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 = (msgLen<namelen)? msgLen:(namelen-1);
    memcpy(name,client[index].name,copyLen);
    name[copyLen]='/0';
  }
  pthread_mutex_unlock(&mutex);
  return index!=-1;
}
int Clients::GetAllSocket(int* sockArray, int arrayLen ) {
  pthread_mutex_lock(&mutex);
  int copyCount = arrayLen>clientCount ? clientCount : arrayLen;
  for(int i=0; i<copyCount; i++){
    sockArray[i] = client[i].sock;
  }
  pthread_mutex_unlock(&mutex);
  return copyCount;
}

聊天室服务器主程序Server.cpp
/*server.c*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <pthread.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#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;i<count;i++){
    close(sock[i]);
  }
  
  printf("server stopped./n");
  
  return 0;
}
void* ListenThread(void*ps){
  int s=*(int*)ps;
  fd_set listenSet;
  int sock;
  struct sockaddr_in clientAddr;
  struct timeval timeout;
  while(!toStop){
     FD_ZERO(&listenSet);
     FD_SET(s,&listenSet);
     timeout.tv_sec = 5;
     timeout.tv_usec = 0;
     int ret = select(s+1,&listenSet,NULL,NULL,&timeout);
     if(toStop){
          printf("ListenThread: exit./n");
          return NULL;
     }
     if(ret==-1){
        printf("ListenThread: select() failed!/n");
     }else if(ret==0){
        printf("ListenThread: select() time out./n");
     }else{
        if(FD_ISSET(s,&listenSet)){
           socklen_t addrlen = sizeof(struct sockaddr_in);
           memset(&clientAddr,0,sizeof(struct sockaddr_in));
           if((sock=accept(s,(struct sockaddr*)&clientAddr,&addrlen))==-1){
               fprintf(stderr,"accept failed./n");
           }
           if(!clients.PutClient(sock,clientAddr)){
               printf("max client limited. MAX_CLIENT=%d/n",MAX_CLIENT);
               close(sock);
           }
           printf("accept a connection from %s:%u/n",
                   inet_ntoa(*(struct in_addr*)&(clientAddr.sin_addr.s_addr)),
                   ntohs(clientAddr.sin_port));
           printf("new socket is: %u/n",sock);
         }
     }
   }
   return NULL;
}
void* RecvThread(void*){
  fd_set readSet;
  int sock[MAX_CLIENT];
  char buf[BUFFER_SIZE];
  struct timeval timeout;
  while(!toStop){
    int count = clients.GetAllSocket(sock,MAX_CLIENT);
    if(count==0){
      sleep(2);
      if(toStop){
        printf("RecvThread: exit./n");
        return NULL;
      }
      continue;  
    }
    FD_ZERO(&readSet);
    int maxfd=0;
    for(int i=0;i<count;i++){
       printf("--%d",sock[i]);
       FD_SET(sock[i],&readSet);
       if(sock[i]>maxfd){
         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; i<count; i++){
         if(FD_ISSET(sock[i],&readSet)){
            int bytes=recv(sock[i],buf,sizeof(buf)-1,0);
            if(bytes==-1){
              printf("RecvThread: recv failed./n");
              clients.RemoveClient(sock[i]);
              close(sock[i]);
            }else if(bytes==0){
              printf("RecvThread: socket closed by the other side./n");
              clients.RemoveClient(sock[i]);
              close(sock[i]);
            }else{
              ProcessMessage(sock[i],buf,sizeof(buf),bytes);
            }
         }
      }
    }
    
  }
 
 return NULL;
}
void* SendThread(void*){
   fd_set writeSet;
   int sock[MAX_CLIENT];
   char buf[BUFFER_SIZE];
   struct timeval timeout;
   while(!toStop){
       int ret = messageBuffer.GetMessage(buf,sizeof(buf));
       printf("get a message from buffer./n");
       if(ret==-1){
          printf("SendThread: exit./n");
          return NULL;
       }
       int count = clients.GetAllSocket(sock,MAX_CLIENT);
       FD_ZERO(&writeSet);
       int maxfd = 0;
       for(int i=0;i<count;i++){
          FD_SET(sock[i],&writeSet);
          if(sock[i]>maxfd){
              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<count;i++){
            if(FD_ISSET(sock[i],&writeSet)){
                int messageLen = strlen(buf);
                int bytes = send(sock[i],buf,messageLen,0);
                if(bytes==-1){
                   printf("SendThread: send() failed./n");
                }else if(bytes!=messageLen){
                   printf("SendThread: send message trunked.");
                }else{
                   //do nothing
                }
            }
         }
       }
   }
   return NULL;
}
void ProcessMessage(int sock,char buf[],int bufsize,int bytes){
  struct sockaddr_in clientAddr;
  if(!clients.GetAddrBySocket(sock,&clientAddr)){
     printf("ProcessMessage: can not find socket address./n");
     return;
  }
  char ipString[16];
  unsigned char *ip = (unsigned char*)&clientAddr.sin_addr.s_addr;
  sprintf(ipString,"%u.%u.%u.%u",*ip,*(ip+1),*(ip+2),*(ip+3));
  unsigned short port = ntohs(clientAddr.sin_port);
  buf[bytes]='/0';
  printf("Message from %s:%d: %s/n",ipString,port,buf);
  const char* CMD_BYE="bye";
  if(strcmp(buf,CMD_BYE)==0){
    send(sock,CMD_BYE,strlen(CMD_BYE),0);
    clients.RemoveClient(sock);
    close(sock);
    printf("%s:%u disconnected./n", ipString, port);
    return;
  }else{
        char bufWithName[BUFFER_SIZE+NAME_LEN];
	char cmdname[5];
        char name[NAME_LEN];
	memcpy(cmdname, buf, 4);
	cmdname[4] = '/0';
        const char* CMD_NAME="name";
	if(strcmp(cmdname,CMD_NAME)==0){
	   char newname[NAME_LEN];
           int nameLen = strlen(buf+5);
           int copyLen;
           if(nameLen>=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



基于WSAAsyncSelect模型实现的聊天室图形客户端


对话框头文件ClientDlg.h
// ClientDlg.h : 头文件
//
#pragma once
#include "afxcmn.h"
#include "afxwin.h"
#define WM_NETWORK WM_USER+100
// CClientDlg 对话框
class CClientDlg : public CDialog
{
// 构造
public:
	CClientDlg(CWnd* pParent = NULL);	// 标准构造函数
// 对话框数据
	enum { IDD = IDD_CLIENT_DIALOG };
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持

// 实现
protected:
	HICON m_hIcon;
	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	CIPAddressCtrl m_ip;
	int m_port;
	CEdit m_allmsg;
	CString m_nickname;
	CString m_message;
	afx_msg void OnBnClickedConnect();
	// 服务器套接字
	SOCKET sock;
	struct sockaddr_in serverAddr;
	CString msg;
	LRESULT OnNetwork(WPARAM wParam, LPARAM lParam);
	void OnConnect(SOCKET s);
	void OnRead(SOCKET s);
	void OnCloseSocket(SOCKET s);
	afx_msg void OnBnClickedSend();
	afx_msg void OnBnClickedDisconnect();
	afx_msg void OnClose();
	afx_msg void OnBnClickedQuit();
	afx_msg void OnBnClickedChangeName();
};

对话框源文件 ClientDlg.cpp
// ClientDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "Client.h"
#include "ClientDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialog
{
public:
	CAboutDlg();
// 对话框数据
	enum { IDD = IDD_ABOUTBOX };
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
// 实现
protected:
	DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()

// CClientDlg 对话框


CClientDlg::CClientDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CClientDlg::IDD, pParent)
	, m_port(8000)
	, m_nickname(_T("microtong"))
	, m_message(_T("hello"))
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CClientDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_IP, m_ip);
	DDX_Text(pDX, IDC_PORT, m_port);
	DDX_Control(pDX, IDC_ALLMESSAGE, m_allmsg);
	DDX_Text(pDX, IDC_NICKNAME, m_nickname);
	DDV_MaxChars(pDX, m_nickname, 30);
	DDX_Text(pDX, IDC_MESSAGE, m_message);
	DDV_MaxChars(pDX, m_message, 1024);
}
BEGIN_MESSAGE_MAP(CClientDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_CONNECT, &CClientDlg::OnBnClickedConnect)
	ON_BN_CLICKED(IDC_SEND, &CClientDlg::OnBnClickedSend)
	ON_BN_CLICKED(IDC_DISCONNECT, &CClientDlg::OnBnClickedDisconnect)
	ON_WM_CLOSE()
	ON_BN_CLICKED(IDC_QUIT, &CClientDlg::OnBnClickedQuit)
	ON_BN_CLICKED(IDC_CHANGENAME, &CClientDlg::OnBnClickedChangeName)
	ON_MESSAGE(WM_NETWORK,&CClientDlg::OnNetwork)
END_MESSAGE_MAP()

// CClientDlg 消息处理程序
BOOL CClientDlg::OnInitDialog()
{
	CDialog::OnInitDialog();
	// 将“关于...”菜单项添加到系统菜单中。
	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);
	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}
	// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标
	// TODO: 在此添加额外的初始化代码
	//为测试方便IP地址初始化为本机IP地址
	m_ip.SetAddress(127,0,0,1);
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}
void CClientDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。
void CClientDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文
		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
		// 使图标在工作矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;
		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}
//当用户拖动最小化窗口时系统调用此函数取得光标显示。
//
HCURSOR CClientDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CClientDlg::OnBnClickedConnect()
{
	UpdateData(TRUE);
	DWORD ip;
	m_ip.GetAddress(ip);
	//初始化套接字DLL
	WSADATA wsa;
	if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){
		MessageBox("套接字初始化失败!");
		return;
	}
	//创建套接字
	if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){
		MessageBox("创建套接字失败!");
		WSACleanup();
		return;
	}
	 ZeroMemory(&serverAddr,sizeof(serverAddr));
	 serverAddr.sin_family = AF_INET;	
	 serverAddr.sin_addr.S_un.S_addr = htonl(ip);
	 serverAddr.sin_port = htons(m_port);
	 //注册感兴趣的网络事件
	 if(WSAAsyncSelect(sock, m_hWnd, WM_NETWORK, FD_CONNECT | FD_READ | FD_CLOSE)==SOCKET_ERROR){
		 MessageBox("注册网络事件失败!");
		 closesocket(sock);
		 WSACleanup();
	 }
	 msg.Format("Connecting to %s:%d/n",inet_ntoa(serverAddr.sin_addr), ntohs(serverAddr.sin_port));
	 m_allmsg.SetSel(32767,32767);
	 m_allmsg.ReplaceSel(msg);
	 if(connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr))==SOCKET_ERROR){
		 int err = WSAGetLastError();
		 if(err==WSAEWOULDBLOCK){
			 msg.Format("Waiting....../n");
			 m_allmsg.SetSel(32767,32767);
			 m_allmsg.ReplaceSel(msg);
		 }else{
			 MessageBox("无法连接到服务器!");
			  closesocket(sock);
			 WSACleanup();
		 }
	 }
	 GetDlgItem(IDC_DISCONNECT)->EnableWindow(TRUE);
	 GetDlgItem(IDC_SEND)->EnableWindow(TRUE);
	 GetDlgItem(IDC_CHANGENAME)->EnableWindow(TRUE);
	 GetDlgItem(IDC_CONNECT)->EnableWindow(FALSE);
	 GetDlgItem(IDC_IP)->EnableWindow(FALSE);
	 GetDlgItem(IDC_PORT)->EnableWindow(FALSE);

}
LRESULT CClientDlg::OnNetwork(WPARAM wParam, LPARAM lParam)
{
	SOCKET s = (SOCKET)wParam;
	WORD netEvent = WSAGETSELECTEVENT(lParam);
	WORD error = WSAGETSELECTERROR(lParam);
	if(error!=0){
		msg.Format("Error code: %d/n",error);
		m_allmsg.SetSel(32767,32767);
		m_allmsg.ReplaceSel(msg);
		if(error==WSAECONNREFUSED){
			msg.Format("Connection refused./n");
		    m_allmsg.SetSel(32767,32767);
		    m_allmsg.ReplaceSel(msg);
			closesocket(sock);
			WSACleanup();
			GetDlgItem(IDC_DISCONNECT)->EnableWindow(FALSE);
			GetDlgItem(IDC_SEND)->EnableWindow(FALSE);
			GetDlgItem(IDC_CHANGENAME)->EnableWindow(FALSE);
			GetDlgItem(IDC_CONNECT)->EnableWindow(TRUE);
			GetDlgItem(IDC_IP)->EnableWindow(TRUE);
			GetDlgItem(IDC_PORT)->EnableWindow(TRUE);
		}
		return -1;
	}
	switch(netEvent){
		case FD_CONNECT:
			OnConnect(s);
			break;
		case FD_READ:
			OnRead(s);
			break;
		case FD_CLOSE:
			OnCloseSocket(s);
			break;
	}
	return 0;
}
void CClientDlg::OnConnect(SOCKET s)
{
	msg.Format("Connected to %s:%d/n",inet_ntoa(serverAddr.sin_addr),ntohs(serverAddr.sin_port));
	m_allmsg.SetSel(32767,32767);
	m_allmsg.ReplaceSel(msg);
}
void CClientDlg::OnRead(SOCKET s)
{
	static char buf[4096];
	int bytes;
	bytes = recv(s, buf, sizeof(buf), 0);
	if(bytes==SOCKET_ERROR || bytes==0){
		msg.Format("Socket error %d/n",WSAGetLastError());
		m_allmsg.SetSel(32767,32767);
	    m_allmsg.ReplaceSel(msg);
		closesocket(s);
		return;
	}
	buf[bytes] = '/0';
	m_allmsg.SetSel(32767,32767);
	m_allmsg.ReplaceSel(buf);
	m_allmsg.SetSel(32767,32767);
	m_allmsg.ReplaceSel("/n");
}
void CClientDlg::OnCloseSocket(SOCKET s)
{
	closesocket(s);
	msg = "Connection closed./n ";
	m_allmsg.SetSel(32767,32767);
	m_allmsg.ReplaceSel(msg);
	GetDlgItem(IDC_DISCONNECT)->EnableWindow(FALSE);
	GetDlgItem(IDC_SEND)->EnableWindow(FALSE);
	GetDlgItem(IDC_CHANGENAME)->EnableWindow(FALSE);
	GetDlgItem(IDC_CONNECT)->EnableWindow(TRUE);
	GetDlgItem(IDC_IP)->EnableWindow(TRUE);
	GetDlgItem(IDC_PORT)->EnableWindow(TRUE);
	WSACleanup();
}
void CClientDlg::OnBnClickedSend()
{
	UpdateData(TRUE);
	int bytes;
	bytes = send(sock,(const char*)m_message,m_message.GetLength(),0);
	if(bytes==SOCKET_ERROR){
		int error = WSAGetLastError();
		if(error==WSAEWOULDBLOCK){
			msg = "Send failed for message: ";
			msg+=m_message;
		}else{
			msg.Format("Connection is down.");
		}
		m_allmsg.SetSel(32767,32767);
		m_allmsg.ReplaceSel(msg);
	}
}
void CClientDlg::OnBnClickedDisconnect()
{
	shutdown(sock,SD_SEND);
}
void CClientDlg::OnClose()
{
	//MessageBox("close");
	closesocket(sock);
	WSACleanup();
	CDialog::OnClose();
}
void CClientDlg::OnBnClickedQuit()
{
	SendMessage(WM_CLOSE);
}
void CClientDlg::OnBnClickedChangeName()
{
	UpdateData(TRUE);
	char buf[100];
	sprintf(buf,"name %s",m_nickname);
	if(send(sock,buf,strlen(buf),0)==SOCKET_ERROR){
		MessageBox("发送改名命令失败!");
		return;
	}
	
}


聊天室Windows控制台客户端

运行命令行为:Client.exe 127.0.0.1 8000

命令行参数分别为要连接的IP地址和端口

//Client.cpp
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>
//引入静态链接库
#pragma comment(lib,"ws2_32.lib")
//缓冲区大小
#define BUFFER_SIZE  4096
//用来修改用户姓名的命令
const char* CMD_NAME="name";
//用来退出系统的命令
const char* CMD_BYE="bye";
//负责接收数据的线程
DWORD WINAPI ReceiveThreadProc( LPVOID lpParameter);
int main(int argc, char* argv[]){
	//判断是否输入了IP地址和端口号
	if(argc!=3){
		printf("Usage: %s IPAddress PortNumber/n",argv[0]);
		exit(-1);
	}
	//把字符串的IP地址转化为u_long
	unsigned long ip;
	if((ip=inet_addr(argv[1]))==INADDR_NONE){
		printf("不合法的IP地址:%s",argv[1]);
		exit(-1);
	}
	//把端口号转化成整数
	short port;
	if((port = atoi(argv[2]))==0){
		printf("端口号有误!");
		exit(-1);
	}
	printf("Connecting to %s:%d....../n",inet_ntoa(*(in_addr*)&ip),port);
	WSADATA wsa;
	//初始化套接字DLL
	if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){
		printf("套接字初始化失败!");
		exit(-1);
	}
	//创建套接字
	SOCKET sock;
	if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){
		printf("创建套接字失败!");
		exit(-1);
	}
	struct sockaddr_in serverAddress;
	memset(&serverAddress,0,sizeof(sockaddr_in));
	serverAddress.sin_family=AF_INET;
	serverAddress.sin_addr.S_un.S_addr = ip;
	serverAddress.sin_port = htons(port);
	//建立和服务器的连接
	if(connect(sock,(sockaddr*)&serverAddress,sizeof(serverAddress))==SOCKET_ERROR){
		printf("建立连接失败!");
		exit(-1);
	}
	//创建从服务器接收数据的线程
	DWORD threadId;
	CreateThread(NULL,0,ReceiveThreadProc,&sock,0,&threadId);
	printf(">");
	char buf[BUFFER_SIZE];
	while(1){
		//从控制台读取一行数据
		gets(buf);
		printf(">");
		
		//发送给服务器
	   if(send(sock,buf,strlen(buf),0)==SOCKET_ERROR){
			printf("发送数据失败!");
			exit(-1);
		}
	
	}
	//清理套接字占用的资源
	WSACleanup();
	return 0;
}
DWORD WINAPI ReceiveThreadProc( LPVOID lpParameter){
	SOCKET s = *(SOCKET*)lpParameter;
	char receiveBuf[BUFFER_SIZE];
	int bytes;
	while(1){
	   	if((bytes=recv(s,receiveBuf,sizeof(receiveBuf),0))==SOCKET_ERROR){
			printf("接收数据失败!/n");
			exit(-1);
		}
		if(bytes==0){
			printf("server is down./n");
			exit(-1);
		}
		receiveBuf[bytes]='/0';
		//如果用户输入了“bye”
		if(strcmp(receiveBuf,CMD_BYE)==0){
			closesocket(s);
			exit(0);
		}
		printf("/n%s/n>",receiveBuf);
	}
}

C++ 客户端 图形 聊天室 WSAAsyncSelect

分享到:
评论加载中,请稍后...
创APP如搭积木 - 创意无限,梦想即时!
回到顶部