局域网qq聊天室

主程序


// QQDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "QQ.h"
#include "QQDlg.h"
#include "afxdialogex.h"
#include "Inc.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
 CAboutDlg();

// 对话框数据
 enum { IDD = IDD_ABOUTBOX };

 protected:
 virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
 DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CQQDlg 对话框


CQQDlg::CQQDlg(CWnd* pParent /*=NULL*/)
 : CDialogEx(CQQDlg::IDD, pParent)
{
 bShowAll = FALSE;
 bShutDown = FALSE;
 m_ListenSock = INVALID_SOCKET;
 m_hListenThread = NULL;
 m_ConnectSock = INVALID_SOCKET;
 m_bIsServer = -1;
 m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
}

void CQQDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialogEx::DoDataExchange(pDX);
 DDX_Control(pDX, IDC_SHOW_MSG, m_MsgEdit);
}

BEGIN_MESSAGE_MAP(CQQDlg, CDialogEx)
 ON_WM_SYSCOMMAND()
 ON_WM_PAINT()
 ON_WM_QUERYDRAGICON()
 ON_BN_CLICKED(IDC_NETSET, &CQQDlg::OnBnClickedNetset)
 ON_BN_CLICKED(IDC_START_SERVER, &CQQDlg::OnBnClickedStartServer)
 ON_BN_CLICKED(IDC_START_CLIENT, &CQQDlg::OnBnClickedStartClient)
 ON_BN_CLICKED(IDC_SENDMSG, &CQQDlg::OnBnClickedSendmsg)
 ON_EN_CHANGE(IDC_INPUT_MSG, &CQQDlg::OnEnChangeInputMsg)
 ON_BN_CLICKED(IDC_STOP_CLIENT, &CQQDlg::OnBnClickedStopClient)
 ON_BN_CLICKED(IDC_STOP_SERVER, &CQQDlg::OnBnClickedStopServer)
 ON_BN_CLICKED(IDC_RADIO_CLIENT, &CQQDlg::OnBnClickedRadioClient)
 ON_BN_CLICKED(IDC_RADIO_SERVER, &CQQDlg::OnBnClickedRadioServer)
 ON_BN_CLICKED(IDCANCEL, &CQQDlg::OnBnClickedCancel)
 ON_BN_CLICKED(IDC_OTHER, &CQQDlg::OnBnClickedOther)
 ON_COMMAND(ID_MENU_TrayIcon, &CQQDlg::OnMenuTrayicon)
 ON_MESSAGE(WM_TRAYICON_MSG, OnTrayCallBackMsg)
 ON_COMMAND(ID_SHOW, &CQQDlg::OnShow)
 ON_COMMAND(ID_MENU_EXIT, &CQQDlg::OnMenuExit)
END_MESSAGE_MAP()


// CQQDlg 消息处理程序

BOOL CQQDlg::OnInitDialog()
{
 CDialogEx::OnInitDialog();

 // 将“关于...”菜单项添加到系统菜单中。

 // IDM_ABOUTBOX 必须在系统命令范围内。
 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX < 0xF000);

 CMenu* pSysMenu = GetSystemMenu(FALSE);
 if (pSysMenu != NULL)
 {
  BOOL bNameValid;
  CString strAboutMenu;
  bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
  ASSERT(bNameValid);
  if (!strAboutMenu.IsEmpty())
  {
   pSysMenu->AppendMenu(MF_SEPARATOR);
   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }

 // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
 //  执行此操作
 SetIcon(m_hIcon, TRUE);   // 设置大图标
 SetIcon(m_hIcon, FALSE);  // 设置小图标

 DlgAllInit();
 ExtendDialog(FALSE);
 // TODO: 在此添加额外的初始化代码

 return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CQQDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
 if ((nID & 0xFFF0) == IDM_ABOUTBOX)
 {
  CAboutDlg dlgAbout;
  dlgAbout.DoModal();
 }
 else
 {
  CDialogEx::OnSysCommand(nID, lParam);
 }
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CQQDlg::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
 {
  CDialogEx::OnPaint();
 }
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CQQDlg::OnQueryDragIcon()
{
 return static_cast<HCURSOR>(m_hIcon);
}

void CQQDlg::DlgAllInit()
{
 //EnableWindow(this, FALSE);整个窗口无效
 /*
 void CheckRadioButton(int nIDFirstButton, int nIDLastButton, int nIDCheckButton); //选中某个单选框
 */
 CheckRadioButton(IDC_RADIO_CLIENT, IDC_RADIO_SERVER, IDC_RADIO_CLIENT);//最后一个选中;
 SetDlgItemText(IDC_IP_ADDR, _T("127.0.0.1"));
 SetDlgItemText(IDC_CONNECT_PORT, _T("5566"));
 SetDlgItemText(IDC_LISTEN_PORT, _T("5566"));
 EnableWindow(IDC_STOP_CLIENT, FALSE);
 EnableWindow(IDC_LISTEN_PORT, FALSE);
 EnableWindow(IDC_STOP_SERVER, FALSE);
 EnableWindow(IDC_START_SERVER, FALSE);
 EnableWindow(IDC_STATIC_LISTEN_PORT, FALSE);
 EnableWindow(IDC_SENDMSG,FALSE);
 //AfxMessageBox(_T("hello VA_X"));
 EnableWindow(IDC_SENDMSG, FALSE);
}
BOOL CQQDlg::EnableWindow(UINT uID, BOOL bEnable)
{
 /*
 三种方法实现按钮的启用 or 禁用 --- 实际原理是一样的
 */
 /*********************************************************************************/
 /*
 BOOL EnableWindow(BOOL bEnable = TRUE); //启用 or 禁用某个窗口
 CWnd* GetDlgItem(int nID) //获得某窗口的指针
 HWND GetSafeHwnd() //获得某窗口的句柄
 */
 
 return GetDlgItem(uID)->EnableWindow(bEnable);
 //::EnableWindow(GetDlgItem(IDC_START_SERVER)->GetSafeHwnd(),bEnable);

}
void CQQDlg::ExtendDialog(BOOL bShow)
{
 /*
 生成一个位置、长和宽都是0的CRect类对象
 这一般用来做CRect对象初始化用的.
 第一个参数是矩形左上角点的X,第二个参数是矩形左上角点的Y,第三个是X轴的长度,第四个是Y轴的长度。
 */
 static CRect m_DlgRectLarge(0,0,0,0);
 static CRect m_DlgRectSmall(0,0,0,0);
 static CRect m_GroupRectLarge(0 ,0,0,0);
 static CRect m_GroupRectSmall(0,0,0,0);
 if (m_DlgRectLarge.IsRectNull())
 {
  //每一次都重新设置窗口的大小
  GetWindowRect(&m_DlgRectLarge);//获取窗口总大小
  m_DlgRectSmall = m_DlgRectLarge;
  m_DlgRectSmall.right-= 250;
  //获取当前IDC_FRAME的窗口句柄
  ::GetWindowRect(GetDlgItem(IDC_FRAME)->GetSafeHwnd(),&m_GroupRectLarge);
  m_GroupRectSmall = m_GroupRectLarge;
  m_GroupRectSmall.right-= 250;
 }
 //加上::表示此函数不属于任何类,而是Windows API函数
 if (bShow)
 {
  bShowAll = TRUE;
  SetWindowPos(NULL,0,0,m_DlgRectLarge.Width(),m_DlgRectLarge.Height(),SWP_NOZORDER|SWP_NOMOVE);
  ::SetWindowPos(GetDlgItem(IDC_FRAME)->GetSafeHwnd(),NULL,0,0,m_GroupRectLarge.Width(),m_GroupRectLarge.Height(),SWP_NOMOVE|SWP_NOZORDER);
 }
 else
 {
  bShowAll = FALSE;                             
  SetWindowPos(NULL,0,0,m_DlgRectSmall.Width(),m_DlgRectSmall.Height(),SWP_NOZORDER|SWP_NOMOVE);
  ::SetWindowPos(GetDlgItem(IDC_FRAME)->GetSafeHwnd(),NULL,0,0,m_GroupRectSmall.Width(),m_GroupRectSmall.Height(),SWP_NOMOVE|SWP_NOZORDER);
 }
}

void CQQDlg::OnBnClickedNetset()
{
 // TODO: 在此添加控件通知处理程序代码
 //ShellExecute(NULL,_T("open"),_T("www.baidu.com"),NULL,NULL,SW_SHOWNORMAL);
 if (bShowAll)
 {
  ExtendDialog(FALSE);
 }
 else
 {
  ExtendDialog(TRUE);
  
 }
}
void CQQDlg::ShowMsg(CString strMsg)
{
 m_MsgEdit.SetSel(-1, -1);
 m_MsgEdit.ReplaceSel(strMsg+_T("\r\n"));
}
void CQQDlg::SendClientsMsg(CString strMsg, CClientItem *pNotSend)
{
 TCHAR szBuf[MAX_BUF_SIZE] = {0};
 _tcscpy_s(szBuf, MAX_BUF_SIZE, strMsg);
 for( INT_PTR idx = 0; idx < m_ClientArray.GetCount(); idx++ ) {
  if ( !pNotSend || pNotSend->m_Socket != m_ClientArray.GetAt(idx).m_Socket || pNotSend->hThread != m_ClientArray.GetAt(idx).hThread ||
   pNotSend->m_strIp != m_ClientArray.GetAt(idx).m_strIp) {
    send(m_ClientArray.GetAt(idx).m_Socket, (char *)szBuf, _tcslen(szBuf)*sizeof(TCHAR), 0);
  }
 }
}
void CQQDlg::RemoveClientFromArray(CClientItem in_Item)
{
 for( int idx = 0; idx <m_ClientArray.GetCount(); idx++ ) {
  CClientItem tItem = m_ClientArray.GetAt(idx);
  if ( tItem.m_Socket == in_Item.m_Socket &&
   tItem.hThread == in_Item.hThread &&
   tItem.m_strIp == in_Item.m_strIp ) {
    m_ClientArray.RemoveAt(idx);
  }
 }
}

void CQQDlg::OnBnClickedStartServer()
{
 CreateThread(NULL, 0, ListenThreadFunc, this,0, NULL);
 // TODO: 在此添加控件通知处理程序代码
 /*m_ListenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 if ( m_ListenSock == INVALID_SOCKET ) {
  AfxMessageBox(_T("新建SOCKET失败!"));
  return;
 }
 UINT uPort = GetDlgItemInt(IDC_LISTEN_PORT);
 if ( uPort <= 0 || uPort > 65535 ) {
  AfxMessageBox(_T("请输入合适的端口:1 - 65535"));
  goto __Error_End;
 }
 sockaddr_in service;
 service.sin_family = AF_INET;
 service.sin_addr.s_addr = INADDR_ANY;
 service.sin_port = htons(uPort);
 if (bind(m_ListenSock, (sockaddr*)&service,sizeof(sockaddr_in)) == SOCKET_ERROR)
 {
  AfxMessageBox(_T("绑定端口失败!"));
  goto __Error_End;
 }
 if (listen(m_ListenSock,5) == SOCKET_ERROR)
 {
  AfxMessageBox(_T("监听失败!"));
  goto __Error_End;
 }
 sockaddr_in clientAddr;
 int iLen = sizeof(sockaddr_in);
 SOCKET accSock = accept(m_ListenSock, (struct sockaddr *)&clientAddr, &iLen);
 if (accSock == INVALID_SOCKET)
 {
  AfxMessageBox(_T("接受客户端失败!"));
  goto __Error_End;
 }

__Error_End:
 closesocket(m_ListenSock);
 */
}


void CQQDlg::OnBnClickedStartClient()
{
 // TODO: 在此添加控件通知处理程序代码
 m_hConnectThred = CreateThread(NULL, 0, ConnectThreadFunc, this, 0, NULL);
}


void CQQDlg::OnBnClickedSendmsg()
{
 // TODO: 在此添加控件通知处理程序代码

 CString strMsg;
 GetDlgItemText(IDC_INPUT_MSG, strMsg);
 if ( m_bIsServer == TRUE ) {
  strMsg = _T("服务器:>") + strMsg;
  ShowMsg(strMsg);
  SendClientsMsg(strMsg);
 }else if (m_bIsServer == FALSE) {
  CString strTmp = _T("本地客户端:>") + strMsg;
  ShowMsg(strTmp);
  int iSend = send(m_ConnectSock, (char *)strMsg.GetBuffer(), strMsg.GetLength()*sizeof(TCHAR), 0);
  strMsg.ReleaseBuffer();
 }
 else{
  AfxMessageBox(_T("请你先进入聊天室!"));
 }
 SetDlgItemText(IDC_INPUT_MSG, _T(""));

}


void CQQDlg::OnEnChangeInputMsg()
{
 // TODO:  如果该控件是 RICHEDIT 控件,它将不
 // 发送此通知,除非重写 CDialogEx::OnInitDialog()
 // 函数并调用 CRichEditCtrl().SetEventMask(),
 // 同时将 ENM_CHANGE 标志“或”运算到掩码中。

 // TODO:  在此添加控件通知处理程序代码
 CString strMsg;
 GetDlgItemText(IDC_INPUT_MSG, strMsg);
 if (strMsg.IsEmpty())
 {
  EnableWindow(IDC_SENDMSG, FALSE);
 }
 else
 {
  EnableWindow(IDC_SENDMSG, TRUE);
 }
}
void CQQDlg::StopClient()
{
 bShutDown = TRUE;
 DWORD dwRet = WaitForSingleObject(m_hConnectThred, 1000);
 if ( dwRet != WAIT_OBJECT_0 ) {
  TerminateThread(m_hConnectThred, -1);
  closesocket(m_ConnectSock);
 }
 m_hConnectThred = NULL;
 m_ConnectSock = INVALID_SOCKET;
 m_bIsServer = -1;
 bShutDown = FALSE;
}
void CQQDlg::StopServer()
{
 UINT nCount = m_ClientArray.GetCount();
 HANDLE *m_pHandles = new HANDLE[nCount+1];
 m_pHandles[0] = m_hListenThread;
 for( int idx = 0; idx < nCount; idx++ ) {
  m_pHandles[idx+1] = m_ClientArray.GetAt(idx).hThread;
 }
 bShutDown = TRUE;
 DWORD dwRet = WaitForMultipleObjects(nCount+1, m_pHandles, TRUE, 1000);
 if ( dwRet != WAIT_OBJECT_0 ) {
  for( INT_PTR i = 0; i < m_ClientArray.GetCount(); i++ ) {
   TerminateThread(m_ClientArray.GetAt(i).hThread, -1);
   closesocket(m_ClientArray.GetAt(i).m_Socket);
  }
  TerminateThread(m_hListenThread, -1);
  closesocket(m_ListenSock);
 }
 delete [] m_pHandles;
 m_hListenThread = NULL;
 m_ListenSock = INVALID_SOCKET;
 m_bIsServer = -1;
 bShutDown = FALSE;
}


void CQQDlg::OnBnClickedStopClient()
{
 // TODO: 在此添加控件通知处理程序代码
 StopClient();
 ShowMsg(_T("停止客户端成功!"));
 EnableWindow(IDC_START_CLIENT);
 EnableWindow(IDC_STOP_CLIENT, FALSE);
}


void CQQDlg::OnBnClickedStopServer()
{
 // TODO: 在此添加控件通知处理程序代码
 StopServer();
 ShowMsg(_T("停止服务器成功!"));
 EnableWindow(IDC_START_SERVER);
 EnableWindow(IDC_STOP_SERVER, FALSE);
}


void CQQDlg::OnBnClickedRadioClient()
{
 // TODO: 在此添加控件通知处理程序代码
 int iRet = -1;
 if ( m_bIsServer == TRUE ) {
  int iRet = MessageBox(_T("您是聊天室的服务器端,如果您退出,所有的客户端都将掉线!\r\n您确定退出吗?"), _T("提示"), MB_OKCANCEL | MB_ICONWARNING);
  if ( iRet == IDOK ) {
   StopServer();
  }else{
   CheckRadioButton(IDC_RADIO_CLIENT, IDC_RADIO_SERVER, IDC_RADIO_SERVER);
  }
 }
 if ( iRet == IDOK || m_bIsServer == -1 ) {
  EnableWindow(IDC_IP_ADDR);
  EnableWindow(IDC_CONNECT_PORT);
  EnableWindow(IDC_STATIC_SERVER_IP);
  EnableWindow(IDC_STATIC_SERVER_PORT);
  EnableWindow(IDC_START_CLIENT);
  EnableWindow(IDC_STOP_CLIENT, FALSE);

  EnableWindow(IDC_LISTEN_PORT, FALSE);
  EnableWindow(IDC_STOP_SERVER, FALSE);
  EnableWindow(IDC_START_SERVER, FALSE);
  EnableWindow(IDC_STATIC_LISTEN_PORT, FALSE);
 }
}


void CQQDlg::OnBnClickedRadioServer()
{
 // TODO: 在此添加控件通知处理程序代码
 int iRet = -1;
 if ( m_bIsServer == FALSE ) {
  int iRet = MessageBox(_T("您正在聊天室中,确定退出吗?"), _T("提示"), MB_OKCANCEL | MB_ICONWARNING);
  if ( iRet == IDOK ) {
   StopClient();
  }else{
   CheckRadioButton(IDC_RADIO_CLIENT, IDC_RADIO_SERVER, IDC_RADIO_CLIENT);
  }
 }
 if ( iRet == IDOK || m_bIsServer == -1) {
  EnableWindow(IDC_LISTEN_PORT);
  EnableWindow(IDC_STOP_SERVER, FALSE);
  EnableWindow(IDC_START_SERVER);
  EnableWindow(IDC_STATIC_LISTEN_PORT);

  EnableWindow(IDC_IP_ADDR, FALSE);
  EnableWindow(IDC_CONNECT_PORT, FALSE);
  EnableWindow(IDC_STATIC_SERVER_IP, FALSE);
  EnableWindow(IDC_STATIC_SERVER_PORT, FALSE);
  EnableWindow(IDC_START_CLIENT, FALSE);
  EnableWindow(IDC_STOP_CLIENT, FALSE);
 }
}


void CQQDlg::OnBnClickedCancel()
{
 // TODO: 在此添加控件通知处理程序代码
 if ( m_bIsServer == TRUE ) {
  StopServer();
 }else if ( m_bIsServer == FALSE ) {
  StopClient();
 }
 CDialogEx::OnCancel();
}


void CQQDlg::OnBnClickedOther()
{
 // TODO: 在此添加控件通知处理程序代码
 CPoint pt;
 CRect mRect;
 CMenu mMenu, *pMenu = NULL;
 GetDlgItem(IDC_OTHER)->GetWindowRect(&mRect);
 pt = mRect.BottomRight();
 pt.y = mRect.top+5;
 mMenu.LoadMenu(IDR_MENU1);
 pMenu = mMenu.GetSubMenu(0);
 pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
}

BOOL CQQDlg::TrayMyIcon(BOOL bAdd)
{
 BOOL bRet = FALSE;
 NOTIFYICONDATA tnd;
 tnd.cbSize = sizeof(NOTIFYICONDATA);
 tnd.hWnd = GetSafeHwnd();
 tnd.uID = IDI_ICON1;
 if ( bAdd == TRUE ) {
  tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
  tnd.uCallbackMessage = WM_TRAYICON_MSG;
  tnd.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME));
  _tcscpy_s(tnd.szTip, sizeof(tnd.szTip), _T("聊天室v1.0"));
  ShowWindow(SW_MINIMIZE);
  ShowWindow(SW_HIDE);
  bRet = Shell_NotifyIcon(NIM_ADD, &tnd);
 }else{
  ShowWindow(SW_SHOWNA);
  SetForegroundWindow();
  bRet = Shell_NotifyIcon(NIM_DELETE, &tnd);
 }
 return bRet;
}


void CQQDlg::OnMenuTrayicon()
{
 // TODO: 在此添加命令处理程序代码
 TrayMyIcon();

}


LRESULT CQQDlg::OnTrayCallBackMsg(WPARAM wparam, LPARAM lparam)
{
 switch(lparam)
 {
 case WM_RBUTTONUP:
  {
   CMenu mMenu, *pMenu = NULL;
   CPoint pt;
   mMenu.LoadMenu(IDR_MENU2);
   pMenu = mMenu.GetSubMenu(0);
   GetCursorPos(&pt);
   SetForegroundWindow();
   pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
   break;
  }
 case WM_LBUTTONDBLCLK:
  ShowWindow(SW_RESTORE);
  SetForegroundWindow();
  TrayMyIcon(FALSE);
  break;
 default:break;
 }
 return NULL;
}


void CQQDlg::OnShow()
{
 // TODO: 在此添加命令处理程序代码
 ShowWindow(SW_RESTORE);
 TrayMyIcon(FALSE);
}


void CQQDlg::OnMenuExit()
{
 // TODO: 在此添加命令处理程序代码
 TrayMyIcon(FALSE);
 OnBnClickedCancel();
}

#include "stdafx.h"
#include "QQDlg.h"
BOOL SOCKET_Select(SOCKET hSock, int nTimeOut, BOOL bRead)
{
 fd_set fdset;
 timeval tv;
 FD_ZERO(&fdset);
 FD_SET(hSock, &fdset);
 nTimeOut = nTimeOut > 1000 ? 1000:nTimeOut;
 tv.tv_sec = 0;
 tv.tv_usec = nTimeOut;
 int iRet = 0;
 if (bRead)
 {
  iRet = select(0, &fdset, NULL, NULL, &tv);
 }
 else
 {
  iRet = select(0,NULL, &fdset, NULL,&tv);
 }
 if (iRet < 0)
 {
  return FALSE;
 }
 else if (FD_ISSET(hSock, &fdset))
 {
  return TRUE;
 }
}
DWORD WINAPI ListenThreadFunc(LPVOID pParam)
{
 CQQDlg *pChatRoom = (CQQDlg *)pParam;
 ASSERT(pChatRoom != NULL);
 pChatRoom->m_ListenSock = socket(AF_INET , SOCK_STREAM , IPPROTO_TCP);
 if ( pChatRoom->m_ListenSock == INVALID_SOCKET ) {
  AfxMessageBox(_T("新建Socket失败!"));
  return FALSE;
 }

 int iPort = pChatRoom->GetDlgItemInt(IDC_LISTEN_PORT);
 if ( iPort <= 0 || iPort > 65535 ) {
  AfxMessageBox(_T("请输入合适的端口:1 - 65535"));
  goto __Error_End;
 }

 sockaddr_in service;
 service.sin_family = AF_INET;
 service.sin_addr.s_addr = INADDR_ANY;
 service.sin_port = htons(iPort);

 if ( bind(pChatRoom->m_ListenSock, (sockaddr*)&service, sizeof(sockaddr_in)) == SOCKET_ERROR ) {
  AfxMessageBox(_T("绑定端口失败!"));
  goto __Error_End;
 }

 if( listen(pChatRoom->m_ListenSock, 5) == SOCKET_ERROR ) {
  AfxMessageBox(_T("监听失败!"));
  goto __Error_End;
 }
 pChatRoom->ShowMsg(_T("系统信息:启动服务器成功!"));
 pChatRoom->m_bIsServer = TRUE;
 pChatRoom->EnableWindow(IDC_START_SERVER, FALSE);
 pChatRoom->EnableWindow(IDC_STOP_SERVER);
 while( TRUE && !(pChatRoom->bShutDown)) {
  if ( SOCKET_Select(pChatRoom->m_ListenSock, 100, TRUE) ) {
   sockaddr_in clientAddr;
   int iLen = sizeof(sockaddr_in);
   SOCKET accSock = accept(pChatRoom->m_ListenSock, (struct sockaddr *)&clientAddr , &iLen);
   if (accSock == INVALID_SOCKET) {
    continue;
   }
   CClientItem tItem;
   tItem.m_Socket = accSock;
   tItem.m_pMainWnd = pChatRoom;
   tItem.m_strIp = inet_ntoa(clientAddr.sin_addr);//网络字节序转化为本机点分IP地址
   INT_PTR idx = pChatRoom->m_ClientArray.Add(tItem);
   tItem.hThread = CreateThread(NULL, 0, ClientThreadProc, &(pChatRoom->m_ClientArray.GetAt(idx)), CREATE_SUSPENDED, NULL);
   pChatRoom->m_ClientArray.GetAt(idx).hThread = tItem.hThread;

   /*恢复线程的句柄使用该函数能够激活线程的运行*/
   ResumeThread(tItem.hThread);
   CString strMsg;
   strMsg = _T("客户端") + tItem.m_strIp + _T("进入聊天室!");
   pChatRoom->ShowMsg(strMsg);

   pChatRoom->SendClientsMsg(strMsg, &tItem);
   //pChatRoom->SendMessage(strMsg, &tItem);
   //做一些其他操作……
   Sleep(100);
  }
 }

__Error_End:
 closesocket(pChatRoom->m_ListenSock);
 return TRUE;
}
DWORD WINAPI ClientThreadProc(LPVOID lpParameter)
{
 CString strMsg;
 CClientItem m_ClientItem = *(CClientItem *)lpParameter;
 while( TRUE && !(m_ClientItem.m_pMainWnd->bShutDown)) {
  if ( SOCKET_Select(m_ClientItem.m_Socket, 100, TRUE) ) {
   TCHAR szBuf[MAX_BUF_SIZE] = {0};
   int iRet = recv(m_ClientItem.m_Socket, (char *)szBuf, MAX_BUF_SIZE, 0);
   if ( iRet > 0 ) {
    //right;
    strMsg = szBuf;
    strMsg = _T("客户端:") + m_ClientItem.m_strIp + _T(">") + strMsg;
    m_ClientItem.m_pMainWnd->ShowMsg(strMsg);
    m_ClientItem.m_pMainWnd->SendClientsMsg(strMsg, &m_ClientItem);
   }else{
    //close socket;
    strMsg = _T("客户端:") + m_ClientItem.m_strIp + _T(" 离开了聊天室!");
    m_ClientItem.m_pMainWnd->ShowMsg(strMsg);
    m_ClientItem.m_pMainWnd->RemoveClientFromArray(m_ClientItem);
    m_ClientItem.m_pMainWnd->SendClientsMsg(strMsg, &m_ClientItem);
    break;
   }
  }
  Sleep(500);
 }
 return TRUE;
}

#include "stdafx.h"
#include "QQDlg.h"
#include "Inc.h"
DWORD WINAPI ConnectThreadFunc(LPVOID pParam)
{
 CQQDlg *pChatRoom = (CQQDlg *)pParam;
 ASSERT(pChatRoom != NULL);
 pChatRoom->m_ConnectSock = socket(AF_INET , SOCK_STREAM , IPPROTO_TCP);
 if ( pChatRoom->m_ConnectSock == INVALID_SOCKET ) {
  AfxMessageBox(_T("新建Socket失败!"));
  return FALSE;
 }
 CString strServIp;
 pChatRoom->GetDlgItemText(IDC_IP_ADDR, strServIp);
 int iPort = pChatRoom->GetDlgItemInt(IDC_CONNECT_PORT);
 if ( iPort <= 0 || iPort > 65535 ) {
  AfxMessageBox(_T("请输入合适的端口:1 - 65535"));
  goto __Error_End;
 }
 char szIpAddr[16] = {0};
 USES_CONVERSION;
 strcpy_s(szIpAddr, 16, T2A(strServIp));

 sockaddr_in server;
 server.sin_family = AF_INET;
 server.sin_port = htons(iPort);
 server.sin_addr.s_addr = inet_addr(szIpAddr);
 if ( connect(pChatRoom->m_ConnectSock, (struct sockaddr *)&server,  sizeof(struct sockaddr)) == SOCKET_ERROR ) {
  AfxMessageBox(_T("连接失败,请重试!"));
  goto __Error_End;
 }
 pChatRoom->m_bIsServer = FALSE;
 pChatRoom->ShowMsg(_T("系统信息:连接服务器成功!"));
 while( TRUE&&!(pChatRoom->bShutDown)) {
  if ( SOCKET_Select(pChatRoom->m_ConnectSock) ) {
   TCHAR szBuf[MAX_BUF_SIZE] = {0};
   int iRet = recv(pChatRoom->m_ConnectSock, (char *)szBuf, MAX_BUF_SIZE, 0);
   if ( iRet > 0 ) {
    //right;
    pChatRoom->ShowMsg(szBuf);
   }else{
    //close socket;
    pChatRoom->ShowMsg(_T("聊天室服务器已停止,请重新进行连接!"));
    break;
   }
  }
  Sleep(500);
 }

__Error_End:
 closesocket(pChatRoom->m_ConnectSock);
 return TRUE;
}

#pragma once
class CQQDlg;
#define MAX_BUF_SIZE 1024
#define WM_TRAYICON_MSG (WM_USER+100)
class CClientItem
{
 public:
  CString m_strIp;
  SOCKET m_Socket;
  HANDLE hThread;
  CQQDlg *m_pMainWnd;
  CClientItem()
  {
   m_pMainWnd = NULL;
   m_Socket = INVALID_SOCKET;
   hThread =NULL;
  }
};
DWORD WINAPI ListenThreadFunc(LPVOID pParam);
DWORD WINAPI ClientThreadProc(LPVOID lpParameter);
DWORD WINAPI ConnectThreadFunc(LPVOID pParam);
BOOL SOCKET_Select(SOCKET hSocket, int nTimeOut = 100, BOOL bRead = FALSE);

猜你喜欢

转载自blog.csdn.net/u014162133/article/details/46573917