sockt练习-文本聊天c/s实现

来源:互联网 发布:网络大专可以做人事吗 编辑:程序博客网 时间:2024/06/07 00:39

前言
这几天,大家都在做这个练习。
有的同学做的很快,1,2天就搞定(还不是整个工作日). 我很疑惑, 也很惊奇 ^_^
像这样的小项目,一般我需要4个完整工作日(思考,规划,编码, 测试, 修改BUG, 完善).
再短的时限,臣做不到啊^_^
服务端:win32控制台(SDK版)
客户端 : MFCDialog
功能: 群聊,私聊,用户在线列表,登入,登出,心跳.
这里写图片描述
统计了一下代码行数, 从侧面看看自己有多挫~
这里写图片描述
知识点
* 以前以为用异步操作比同步操作好,退出速度快。现在才知道,原来在另外一条线程中,将对象句柄关了,她就失败退出了. 这样,可以用同步方法在线程中做事,逻辑简单. 对于远程文件操作或socket操作,均可以用将句柄关了的方法,让阻塞操作退出. 停止或退出操作很快.
* 这次,也是只靠本地msdn从头写的, 已经习惯在vc6中没有代码提示和语法高亮的环境. 代码熟练度也提高了一些. 感谢好心人给了在Win7X64下可用的SDK补丁, 在Win7X64下的vc6中,也可以用新的API了.
* 在C/S传递参数的过程中,我使用了固定头结构+柔性数组. 用字符串按照json或xml传递维护性较好,但是那要用第三方的xml库或json库, 没用. 如果是在正式工程中, 可以采用字符串来传递全部参数. 如果要保密,还要考虑传输加密问题. 练手的东西,就练习一下socket操作就好了:P
* 线程操作类,我又重新封装了一下, 对_beginthreadex的操作更方便了.
* 将发送操作和接收操作分开在发送线程和接收线程, 可以防止对同一个Socket读写操作时,不同线程同时读或同时写引起的内容非预期问题. 也能简化业务逻辑的流程.

客户端socket操作流程
** 初始化Socket环境, call WSAStartup
** 建立Socket对象, 得到Socket句柄. call socket
** 连接服务器. call connect
** 在接收线程中读取服务器数据. call recv(可以封装成为recvEx, 考虑一次接收不完的情况)
** 在发送线程中向服务器发送数据. call send(可以封装成为sendEx, 考虑一次发送不完的情况)
** 如果客户端要向服务器发送东西, 将内容压入发送队列,发送线程查到发送队列中有东西,就拿走发送给服务器. 如果一种操作,要进行多次的发送和接收的交互才能完成, 将每次的发送子操作和接收子操作加上帧号.
** 关闭Socket句柄. call closesocket
** 反初始化Socket环境, call WSACleanup
服务器socket操作流程
** 初始化Socket环境, call WSAStartup
** 建立Socket对象, 得到Socket句柄. call socket
** 指定服务器的IP和端口号. call bind
** 指定服务器能处理的客户端数量. call listen
** 建立一条发送线程和一条心跳检测线程, 所有客户端的发送任务和心跳检测都由这两条线程完成.
** 等待客户端连接. call accept
** 如果来了客户端连接, 对每个客户端建立一个接收线程, 启动接收线程. 回到上一步,继续等待新的客户端来连接服务器
** 反初始化Socket环境, call WSACleanup
工程下载点
SocketIM.zip
代码预览
客户端

// ControlCmd.h: interface for the CControlCmd class.////////////////////////////////////////////////////////////////////////#if !defined(AFX_CONTROLCMD_H__099A6E75_DFBB_4E95_992E_7774AEFCDBA9__INCLUDED_)#define AFX_CONTROLCMD_H__099A6E75_DFBB_4E95_992E_7774AEFCDBA9__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000#include "stdafx.h"#define SERVER_HEARTBEAT_CHECK_SPANTIME ((DWORD)10 * 1000) ///< 服务器10秒检查一次心跳#define CLIENT_HEARTBEAT_SPANTIME ((DWORD)5 * 1000) ///< 客户端5秒发一次心跳enum eControlCmd {    eControlCmd_unknown = -1,    eControlCmd_loginByClient, ///< 客户端发起登录    eControlCmd_loginOutByClient, ///< 客户端发起登出    eControlCmd_ImPublic, ///< 群聊    eControlCmd_ImPrivate, ///< 私聊    eControlCmd_OnLine, ///< 用户上线    eControlCmd_DownLine, ///< 用户下线    eControlCmd_UserList, ///< 用户列表    eControlCmd_HeartBeat, ///< 心跳包};#pragma pack(push)#pragma pack(1)typedef struct _tag_ControlCmdData_loginByClient {    TCHAR szUserName[MAX_PATH]; ///< 用户名    TCHAR szPwd[MAX_PATH]; ///< 口令    OUT BOOL bLoginOK; ///< 是否登录成功    OUT DWORD dwUserID; ///< 服务器返回的当前用户ID    OUT TCHAR szDesc[MAX_PATH]; ///< 服务器返回的结果描述    void Clear() {        ::ZeroMemory(this, sizeof(_tag_ControlCmdData_loginByClient));    }    BOOL IsLoginOk() {        return bLoginOK;    }    void SetLoginOk(BOOL bIn) {        bLoginOK = bIn;    }    DWORD GetUserID() {        return dwUserID;    }    TCHAR* GetUserName() {        return szUserName;    }    BOOL IsValidLoginData() {        return ((_tcslen(szUserName) > 0) && (_tcslen(szPwd) > 0));    }    void ShowDetail() {        _tprintf(_T("szUserName = %s\n"), szUserName);        _tprintf(_T("szPwd = %s\n"), szPwd);        _tprintf(_T("bLoginOK = %d\n"), bLoginOK);        _tprintf(_T("dwUserID = 0x%X\n"), dwUserID);        _tprintf(_T("szDesc = %s\n"), szDesc);    }}TAG_CONTROLCMDDATA_LOGINBYCLIENT;typedef struct _tag_ControlCmdData_ImPublic {    DWORD dwUserID; ///< 服务器返回的当前用户ID    TCHAR szUserName[MAX_PATH]; ///< 当前用户名称    TCHAR szMsg[MAX_PATH]; ///< 聊天内容}TAG_CONTROLCMDDATA_IMPUBLIC;typedef struct _tag_ControlCmdData_ImPrivate {    DWORD dwOtherUserID; ///< 对方ID    TCHAR szOtherUserName[MAX_PATH]; ///< 对方用户名称    TAG_CONTROLCMDDATA_IMPUBLIC MyData; ///< 我的ID和聊天内容}TAG_CONTROLCMDDATA_IMPRIVATE;typedef struct _tag_ControlCmdData_HeartBeat {    DWORD dwUserID; ///< 服务器返回的当前用户ID}TAG_CONTROLCMDDATA_HEARTBEAT;typedef struct _tag_ControlCmdData_User_Online {    DWORD dwUserID; ///< 服务器返回的当前用户ID    TCHAR szUserName[MAX_PATH]; ///< 当前用户名称}TAG_CONTROLCMDDATA_USER_ONLINE;typedef struct _tag_ControlCmdData_User_Downline {    DWORD dwUserID; ///< 服务器返回的当前用户ID    TCHAR szUserName[MAX_PATH]; ///< 当前用户名称}TAG_CONTROLCMDDATA_USER_DOWNLINE;typedef struct _tag_ControlCmdData_UserInfoList {    /// 需要更新在线用户列表的用户    DWORD dwUserID; ///< 服务器返回的当前用户ID    TCHAR szUserName[MAX_PATH]; ///< 当前用户名称    /// 用户列表信息    DWORD dwUserOnlineCnt; ///< 用户数量    TAG_CONTROLCMDDATA_USER_ONLINE UserInfoAry[1]; ///< 用户信息数组首地址}TAG_CONTROLCMDDATA_USER_USERINFOLIST;typedef union _tag_CmdData{    TAG_CONTROLCMDDATA_LOGINBYCLIENT LoginInfo; ///< 登录信息    TAG_CONTROLCMDDATA_IMPUBLIC ImPublic; ///< 群聊    TAG_CONTROLCMDDATA_IMPRIVATE ImPrivate; ///< 私聊    TAG_CONTROLCMDDATA_USER_ONLINE UserOnline; ///< 用户上线    TAG_CONTROLCMDDATA_USER_DOWNLINE UserDownline; ///< 用户下线    TAG_CONTROLCMDDATA_USER_USERINFOLIST UserInfoList; ///< 用户信息列表    TAG_CONTROLCMDDATA_HEARTBEAT ImHeartBeat; ///< 心跳}TAG_CMDDATA;typedef struct _tag_ControlCmdData {    eControlCmd Cmd;    TAG_CMDDATA Data;    _tag_ControlCmdData() {        Clear();    }    void Clear() {        Cmd = eControlCmd_unknown;         ZeroMemory(&Data, sizeof(Data));    }}TAG_CONTROLCMDDATA;#pragma pack(pop)#endif // !defined(AFX_CONTROLCMD_H__099A6E75_DFBB_4E95_992E_7774AEFCDBA9__INCLUDED_)
// ClientDlg.h : header file//#if !defined(AFX_CLIENTDLG_H__17DAE0C4_EB5C_43F7_B087_840AE0D9E580__INCLUDED_)#define AFX_CLIENTDLG_H__17DAE0C4_EB5C_43F7_B087_840AE0D9E580__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000#include "stdafx.h"#include <windows.h>#include <winsock2.h>#pragma comment(lib, "Ws2_32.lib")#include <list>using namespace std;#include "ControlCmd.h"#include "MyThread.h"/////////////////////////////////////////////////////////////////////////////// CClientDlg dialog#define WM_SHOW_THREAD_MSG (WM_USER + 1000)class CClientDlg : public CDialog{// Constructionpublic:    CClientDlg(CWnd* pParent = NULL);   // standard constructor    virtual ~CClientDlg();public:    /// member function    void SocketDataInit();    void SocketDataUnInit();    void ShowSocketErrMsg();    void ShowErrMsg();    void ShowErrMsg(int iErrSn);    void ShowMsg(TCHAR* pMsg);    void ShowThreadMsg(TCHAR* pMsg);    void AddUserToList(DWORD dwUserID, TCHAR* pcUserName);    void RemoveUserFromList(DWORD dwUserID, TCHAR* pcUserName);    void FillOtherUserIDFromList();    BOOL IsNeedLogin();    void ConnectOpen();    void ConnectClose();    void UserLogin();    void UserLoginOut();    static unsigned __stdcall ThreadProc_Send(void* pParam);    static unsigned __stdcall ThreadProc_Recv(void* pParam);    unsigned ThreadProc_Send();    unsigned ThreadProc_Recv();    unsigned ThreadProc_Recv(TAG_CONTROLCMDDATA* pCmdData);    unsigned ThreadProc_Recv_loginByClient(TAG_CONTROLCMDDATA* pCmdData);    unsigned ThreadProc_Recv_loginOutByClient(TAG_CONTROLCMDDATA* pCmdData);    unsigned ThreadProc_Recv_ImPublic(TAG_CONTROLCMDDATA* pCmdData);    unsigned ThreadProc_Recv_ImPrivate(TAG_CONTROLCMDDATA* pCmdData);    unsigned ThreadProc_Recv_HeartBeat(TAG_CONTROLCMDDATA* pCmdData);    unsigned ThreadProc_Recv_UserOnline(TAG_CONTROLCMDDATA* pCmdData);    unsigned ThreadProc_Recv_UserList(TAG_CONTROLCMDDATA* pCmdData);    unsigned ThreadProc_Recv_UserDownline(TAG_CONTROLCMDDATA* pCmdData);    void AddDataToList(list<TAG_CONTROLCMDDATA*>& List, TAG_CONTROLCMDDATA* p, CRITICAL_SECTION& cs);    TAG_CONTROLCMDDATA* GetDataFromList(list<TAG_CONTROLCMDDATA*>& List, CRITICAL_SECTION& cs);    void RemoveList(list<TAG_CONTROLCMDDATA*>& List, CRITICAL_SECTION& cs);private:    /// member    WSADATA m_WSAData;    SOCKET m_sClient;    BOOL m_bConnectOk;    /// 服务器返回的登录信息    TAG_CONTROLCMDDATA_LOGINBYCLIENT m_LoginInfoFromServer;    BOOL m_bNeedUpdateLoginInfo;    CMyThread m_ThreadSend;    list<TAG_CONTROLCMDDATA*> m_ListCmdDataToSend;    CRITICAL_SECTION g_csListCmdDataToSend;    list<TAG_CONTROLCMDDATA_USER_ONLINE> m_ListUsersOnlineUpdate;    list<TAG_CONTROLCMDDATA_USER_DOWNLINE> m_ListUsersDownlineUpdate;    list<CString> m_ListDispMsg;    CRITICAL_SECTION g_csDispMsg;    CMyThread m_ThreadRecv;public:// Dialog Data    //{{AFX_DATA(CClientDlg)    enum { IDD = IDD_CLIENT_DIALOG };    CListCtrl   m_ListUsersOnline;    CEdit   m_EditImMsg;    CString m_csImMsg;    CString m_csImMsgToSend;    BOOL    m_bCheckImPrivate;    CString m_strOtherUserID;    CString m_strMyIDFromServer;    CString m_strMyName;    CString m_strMyPwd;    //}}AFX_DATA    // ClassWizard generated virtual function overrides    //{{AFX_VIRTUAL(CClientDlg)    protected:    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support    //}}AFX_VIRTUAL// Implementationprotected:    HICON m_hIcon;    // Generated message map functions    //{{AFX_MSG(CClientDlg)    virtual BOOL OnInitDialog();    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);    afx_msg void OnPaint();    afx_msg HCURSOR OnQueryDragIcon();    afx_msg void OnButtonSendContent();    afx_msg void OnButtonLogin();    afx_msg void OnButtonLoginout();    afx_msg void OnDestroy();    afx_msg void OnShowThreadMsg();    afx_msg void OnButtonClearContent();    afx_msg void OnItemchangedListUsersOnline(NMHDR* pNMHDR, LRESULT* pResult);    afx_msg void OnClickListUsersOnline(NMHDR* pNMHDR, LRESULT* pResult);    //}}AFX_MSG    DECLARE_MESSAGE_MAP()};//{{AFX_INSERT_LOCATION}}// Microsoft Visual C++ will insert additional declarations immediately before the previous line.#endif // !defined(AFX_CLIENTDLG_H__17DAE0C4_EB5C_43F7_B087_840AE0D9E580__INCLUDED_)
// ClientDlg.cpp : implementation file//#include "stdafx.h"#include <windows.h>#include <tchar.h>#include <assert.h>#include "Client.h"#include "ClientDlg.h"#include "SocketOpt.h"#define USE_SERVER_IP FALSE ///< TRUE, use SERVER_IP; FALSE, use SERVER_NAME#define SERVER_NAME _T("localhost")#define SERVER_IP 0x7f000001 ///< 127.0.0.1 0x7f000001#define SERVER_PORT 12345#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif/////////////////////////////////////////////////////////////////////////////// CAboutDlg dialog used for App Aboutclass CAboutDlg : public CDialog{public:    CAboutDlg();    // Dialog Data    //{{AFX_DATA(CAboutDlg)    enum { IDD = IDD_ABOUTBOX };    //}}AFX_DATA    // ClassWizard generated virtual function overrides    //{{AFX_VIRTUAL(CAboutDlg)protected:    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support    //}}AFX_VIRTUAL    // Implementationprotected:    //{{AFX_MSG(CAboutDlg)    //}}AFX_MSG    DECLARE_MESSAGE_MAP()};CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD){    //{{AFX_DATA_INIT(CAboutDlg)    //}}AFX_DATA_INIT}void CAboutDlg::DoDataExchange(CDataExchange* pDX){    CDialog::DoDataExchange(pDX);    //{{AFX_DATA_MAP(CAboutDlg)    //}}AFX_DATA_MAP}BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)//{{AFX_MSG_MAP(CAboutDlg)// No message handlers//}}AFX_MSG_MAPEND_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////// CClientDlg dialogCClientDlg::CClientDlg(CWnd* pParent /*=NULL*/): CDialog(CClientDlg::IDD, pParent){    //{{AFX_DATA_INIT(CClientDlg)    m_csImMsg = _T("");    m_csImMsgToSend = _T("");    m_bCheckImPrivate = FALSE;    m_strOtherUserID = _T("");    m_strMyName = _T("");    m_strMyPwd = _T("");    //}}AFX_DATA_INIT    // Note that LoadIcon does not require a subsequent DestroyIcon in Win32    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);    SocketDataInit();}CClientDlg::~CClientDlg() {    SocketDataUnInit();}void CClientDlg::DoDataExchange(CDataExchange* pDX){    CDialog::DoDataExchange(pDX);    //{{AFX_DATA_MAP(CClientDlg)    DDX_Control(pDX, IDC_LIST_USERS_ONLINE, m_ListUsersOnline);    DDX_Control(pDX, IDC_EDIT_MSG, m_EditImMsg);    DDX_Text(pDX, IDC_EDIT_MSG, m_csImMsg);    DDX_Text(pDX, IDC_EDIT_MSG_TO_SEND, m_csImMsgToSend);    DDX_Check(pDX, IDC_CHECK_IM_PRIVATE, m_bCheckImPrivate);    DDX_Text(pDX, IDC_EDIT_USER2_ID, m_strOtherUserID);    DDX_Text(pDX, IDC_EDIT_MY_NAME, m_strMyName);    DDX_Text(pDX, IDC_EDIT_MY_PWD, m_strMyPwd);    //}}AFX_DATA_MAP}BEGIN_MESSAGE_MAP(CClientDlg, CDialog)//{{AFX_MSG_MAP(CClientDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON_SEND_CONTENT, OnButtonSendContent)    ON_BN_CLICKED(IDC_BUTTON_LOGIN, OnButtonLogin)    ON_BN_CLICKED(IDC_BUTTON_LOGINOUT, OnButtonLoginout)    ON_WM_DESTROY()    ON_MESSAGE(WM_SHOW_THREAD_MSG, OnShowThreadMsg)    ON_BN_CLICKED(IDC_BUTTON_CLEAR_CONTENT, OnButtonClearContent)    ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_USERS_ONLINE, OnItemchangedListUsersOnline)    ON_NOTIFY(NM_CLICK, IDC_LIST_USERS_ONLINE, OnClickListUsersOnline)    //}}AFX_MSG_MAPEND_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////// CClientDlg message handlersBOOL CClientDlg::OnInitDialog(){    CDialog::OnInitDialog();    // Add "About..." menu item to system menu.    // IDM_ABOUTBOX must be in the system command range.    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);        }    }    // Set the icon for this dialog.  The framework does this automatically    //  when the application's main window is not a dialog    SetIcon(m_hIcon, TRUE);         // Set big icon    SetIcon(m_hIcon, FALSE);        // Set small icon    // TODO: Add extra initialization here    m_ThreadSend.SetParam(        ThreadProc_Send,         NULL,         NULL,         this);    m_ThreadRecv.SetParam(        ThreadProc_Recv,         NULL,         NULL,         this);    m_strMyIDFromServer = _T("未登录用户");    this->SetWindowText((LPTSTR)(LPCTSTR)m_strMyIDFromServer);    /// 初始化用户列表    m_ListUsersOnline.InsertColumn(0, _T("ID"), LVCFMT_LEFT);    m_ListUsersOnline.InsertColumn(1, _T("姓名"), LVCFMT_LEFT);    m_ListUsersOnline.SetColumnWidth(0, 60);    m_ListUsersOnline.SetColumnWidth(1, LVSCW_AUTOSIZE_USEHEADER);    m_ListUsersOnline.SetExtendedStyle(    m_ListUsersOnline.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);    return TRUE;  // return TRUE  unless you set the focus to a control}void CClientDlg::AddUserToList(DWORD dwUserID, TCHAR* pcUserName) {    CString str;    int i = 0;    int iRowCnt = m_ListUsersOnline.GetItemCount();    LVITEM lvitem;    BOOL bFind = FALSE;    if (!m_LoginInfoFromServer.IsLoginOk()        || (m_LoginInfoFromServer.GetUserID() == dwUserID)) {        return;    }    for (i = 0; i < iRowCnt; i++) {        if (dwUserID == m_ListUsersOnline.GetItemData(i)) {            bFind = TRUE;            break;        }    }    if (!bFind) {        str.Format(_T("%d"), dwUserID);        lvitem.mask = LVIF_TEXT | LVIF_IMAGE;        lvitem.iItem = iRowCnt;        lvitem.iSubItem = 0;        lvitem.pszText = (LPTSTR)(LPCTSTR)str;        m_ListUsersOnline.InsertItem(&lvitem);         m_ListUsersOnline.SetItemText(iRowCnt, 1, pcUserName);        m_ListUsersOnline.SetItemData(iRowCnt, dwUserID);    }}void CClientDlg::RemoveUserFromList(DWORD dwUserID, TCHAR* pcUserName) {    int i = 0;    int iRowCnt = m_ListUsersOnline.GetItemCount();    if (m_LoginInfoFromServer.GetUserID() == dwUserID) {        return;    }    for (i = 0; i < iRowCnt; i++) {        if (dwUserID == m_ListUsersOnline.GetItemData(i)) {            m_ListUsersOnline.DeleteItem(i);            break;        }    }}void CClientDlg::OnSysCommand(UINT nID, LPARAM lParam){    if ((nID & 0xFFF0) == IDM_ABOUTBOX)    {        CAboutDlg dlgAbout;        dlgAbout.DoModal();    }    else    {        CDialog::OnSysCommand(nID, lParam);    }}// If you add a minimize button to your dialog, you will need the code below//  to draw the icon.  For MFC applications using the document/view model,//  this is automatically done for you by the framework.void CClientDlg::OnPaint() {    if (IsIconic())    {        CPaintDC dc(this); // device context for painting        SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);        // Center icon in client rectangle        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;        // Draw the icon        dc.DrawIcon(x, y, m_hIcon);    }    else    {        CDialog::OnPaint();    }}// The system calls this to obtain the cursor to display while the user drags//  the minimized window.HCURSOR CClientDlg::OnQueryDragIcon(){    return (HCURSOR) m_hIcon;}void CClientDlg::SocketDataInit() {    InitializeCriticalSection(&g_csListCmdDataToSend);    InitializeCriticalSection(&g_csDispMsg);    m_strMyIDFromServer.Empty();    m_bConnectOk = FALSE;    m_LoginInfoFromServer.Clear();    m_bNeedUpdateLoginInfo = FALSE;    m_sClient = INVALID_SOCKET;    WSAStartup(MAKEWORD(2,2), &m_WSAData);}void CClientDlg::SocketDataUnInit() {    WSACleanup();    DeleteCriticalSection(&g_csListCmdDataToSend);    DeleteCriticalSection(&g_csDispMsg);}void CClientDlg::ShowSocketErrMsg() {    int iErrSn = WSAGetLastError();    ShowErrMsg(iErrSn);    ConnectClose(); ///< 防止断线后, 再登录时,登录不上}void CClientDlg::ShowErrMsg() {    int iErrSn = GetLastError();    ShowErrMsg(iErrSn);}void CClientDlg::ShowErrMsg(int iErrSn) {    LPVOID lpMsgBuf = NULL;    FormatMessage(         FORMAT_MESSAGE_ALLOCATE_BUFFER |         FORMAT_MESSAGE_FROM_SYSTEM |         FORMAT_MESSAGE_IGNORE_INSERTS,        NULL,        iErrSn,        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language        (LPTSTR) &lpMsgBuf,        0,        NULL);    if (NULL != lpMsgBuf) {        ShowThreadMsg((LPTSTR)lpMsgBuf);    }    LocalFree(lpMsgBuf);}void CClientDlg::ShowMsg(TCHAR* pMsg) {    DWORD dwSel = 0;    if (NULL != pMsg) {        UpdateData(TRUE);        m_csImMsg += pMsg;        m_csImMsg += _T("\r\n");        UpdateData(FALSE);        m_EditImMsg.LineScroll(65535);    }}void CClientDlg::OnButtonSendContent() {    int iRc = 0;    TAG_CONTROLCMDDATA* pCmdData = NULL;    DWORD dwTotalLen = 0;    int iMsgLen = 0;    do {        if (IsNeedLogin()) {            ShowThreadMsg(_T("请先登录"));            break;        }        UpdateData(TRUE);        if (m_csImMsgToSend.IsEmpty()) {            ShowThreadMsg(_T("发送内容为空, 请输入聊天内容"));            break;        }        if (m_bCheckImPrivate) {            if (m_strOtherUserID.IsEmpty()) {                ShowThreadMsg(_T("私聊需要提供对方用户ID"));                break;            }        }        pCmdData = new TAG_CONTROLCMDDATA;        assert(NULL != pCmdData);        pCmdData->Cmd = m_bCheckImPrivate ? eControlCmd_ImPrivate : eControlCmd_ImPublic;        if (m_bCheckImPrivate) {            pCmdData->Data.ImPrivate.dwOtherUserID = _ttol((LPTSTR)(LPCTSTR)m_strOtherUserID);            pCmdData->Data.ImPrivate.MyData.dwUserID = m_LoginInfoFromServer.GetUserID();            _tcscpy(pCmdData->Data.ImPrivate.MyData.szUserName, m_LoginInfoFromServer.GetUserName());            _tcscpy(pCmdData->Data.ImPrivate.MyData.szMsg, (LPTSTR)(LPCTSTR)m_csImMsgToSend);        } else {            pCmdData->Data.ImPublic.dwUserID = m_LoginInfoFromServer.GetUserID();            _tcscpy(pCmdData->Data.ImPublic.szUserName, m_LoginInfoFromServer.GetUserName());            _tcscpy(pCmdData->Data.ImPublic.szMsg, (LPTSTR)(LPCTSTR)m_csImMsgToSend);        }        AddDataToList(m_ListCmdDataToSend, pCmdData, g_csListCmdDataToSend);        m_csImMsgToSend.Empty();        UpdateData(FALSE);    } while (0);}void CClientDlg::OnButtonLogin() {    /// 连接服务器    if (!m_bConnectOk) {        ConnectOpen();    }    /// 登录    if (m_bConnectOk && !m_LoginInfoFromServer.IsLoginOk()) {        UserLogin();    }}void CClientDlg::OnButtonLoginout() {    /// 登出      if (m_LoginInfoFromServer.IsLoginOk()) {        UserLoginOut();        /// 必须设置标志, 防止服务器掉线后, 登出不了        m_LoginInfoFromServer.SetLoginOk(FALSE);        m_ListUsersOnline.DeleteAllItems();    } else {        ShowThreadMsg(_T("已经是登出状态"));    }}void CClientDlg::UserLogin() {    TAG_CONTROLCMDDATA* pCmdData = NULL;    int iRc = 0;    do {        if (m_LoginInfoFromServer.IsLoginOk()) {            break;        }        UpdateData(TRUE);        if (m_strMyName.IsEmpty() || m_strMyPwd.IsEmpty()) {            ShowThreadMsg(_T("请输入用户名和口令!"));            break;        }        pCmdData = new TAG_CONTROLCMDDATA;        assert(NULL != pCmdData);        pCmdData->Cmd = eControlCmd_loginByClient;        _tcscpy(pCmdData->Data.LoginInfo.szUserName, (LPTSTR)(LPCTSTR)m_strMyName);        _tcscpy(pCmdData->Data.LoginInfo.szPwd, (LPTSTR)(LPCTSTR)m_strMyPwd);        AddDataToList(m_ListCmdDataToSend, pCmdData, g_csListCmdDataToSend);    } while (0);}void CClientDlg::UserLoginOut() {    TAG_CONTROLCMDDATA* pCmdData = NULL;    int iRc = 0;    do {        if (!m_LoginInfoFromServer.IsLoginOk()) {            break;        }        pCmdData = new TAG_CONTROLCMDDATA;        assert(NULL != pCmdData);        pCmdData->Cmd = eControlCmd_loginOutByClient;        pCmdData->Data.LoginInfo = m_LoginInfoFromServer;         AddDataToList(m_ListCmdDataToSend, pCmdData, g_csListCmdDataToSend);    } while (0);}void CClientDlg::ConnectOpen() {    sockaddr_in addrServer;    struct hostent* pHost = NULL;    int iRc = 0;    CString str;    do {        if (INVALID_SOCKET == m_sClient) {            m_sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);            if (INVALID_SOCKET == m_sClient) {                ShowSocketErrMsg();                break;            }        }        addrServer.sin_family = AF_INET;        addrServer.sin_port = htons(SERVER_PORT);        if (USE_SERVER_IP) {            addrServer.sin_addr.S_un.S_addr = htonl(SERVER_IP);        } else {            pHost = gethostbyname(SERVER_NAME);            if (NULL == pHost)            {                ShowSocketErrMsg();                break;            }            CopyMemory(&addrServer.sin_addr, pHost->h_addr_list[0], pHost->h_length);        }        if (!m_bConnectOk) {            iRc = connect(m_sClient, (const struct sockaddr *)&addrServer, sizeof(addrServer));            if (SOCKET_ERROR == iRc) {                ShowSocketErrMsg();                break;            }            str.Format(_T("m_sClient[%d] was Connect to server"), m_sClient);            ShowThreadMsg((LPTSTR)(LPCTSTR)str);            m_bConnectOk = TRUE;            m_ThreadSend.Start();            m_ThreadRecv.Start();        }    } while (0);}void CClientDlg::ConnectClose() {    if (INVALID_SOCKET != m_sClient) {        closesocket(m_sClient);        m_sClient = INVALID_SOCKET;        m_bConnectOk = FALSE;        RemoveList(m_ListCmdDataToSend, g_csListCmdDataToSend);    }}BOOL CClientDlg::IsNeedLogin() {    return (!m_bConnectOk || !m_LoginInfoFromServer.IsLoginOk());}unsigned __stdcall CClientDlg::ThreadProc_Send(void* pParam) {    if (NULL == pParam) {        return S_FALSE;    }    return ((CClientDlg*)pParam)->ThreadProc_Send();}unsigned __stdcall CClientDlg::ThreadProc_Recv(void* pParam) {    if (NULL == pParam) {        return S_FALSE;    }    return ((CClientDlg*)pParam)->ThreadProc_Recv();}unsigned CClientDlg::ThreadProc_Send() {    TAG_CONTROLCMDDATA* p = NULL;    int iRc = FALSE;    CString str;    DWORD dwHeartBeatSpanTime = CLIENT_HEARTBEAT_SPANTIME;    DWORD dwHeartBeatTimeBegin = GetTickCount();    do {        Sleep(1);        if (!m_ThreadSend.cbIsCanContinue()) {            continue;        }        if (m_ThreadSend.cbIsNeedQuit()) {            break;        }        if (m_LoginInfoFromServer.IsLoginOk()) {            if ((GetTickCount() - dwHeartBeatTimeBegin) >= dwHeartBeatSpanTime) {                dwHeartBeatTimeBegin = GetTickCount();                /// 心跳发送间隔到, 制造一个心跳包,发往服务器                p = new TAG_CONTROLCMDDATA;                assert(NULL != p);                p->Cmd = eControlCmd_HeartBeat;                p->Data.ImHeartBeat.dwUserID = m_LoginInfoFromServer.GetUserID();                // str.Format(_T("[%d] Send HeartBeat"), p->Data.ImHeartBeat.dwUserID);                // ShowThreadMsg((LPTSTR)(LPCTSTR)str);                AddDataToList(m_ListCmdDataToSend, p, g_csListCmdDataToSend);            }        }        if ((INVALID_SOCKET != m_sClient) && m_bConnectOk) {            p = GetDataFromList(m_ListCmdDataToSend, g_csListCmdDataToSend);            if (NULL != p) {                iRc = sendEx(m_sClient, (const char*)p, sizeof(TAG_CONTROLCMDDATA), 0);                if (SOCKET_ERROR == iRc) {                    ShowSocketErrMsg();                }                delete p;                p = NULL;            }        }    } while (1);    return S_OK;}unsigned CClientDlg::ThreadProc_Recv() {    TAG_CONTROLCMDDATA CmdData;    int iRc = FALSE;    int iErrSnPrev = WSAGetLastError();    do {        Sleep(1);        if (!m_ThreadRecv.cbIsCanContinue()) {            continue;        }        if (m_ThreadRecv.cbIsNeedQuit()) {            break;        }        if ((INVALID_SOCKET != m_sClient) && m_bConnectOk) {            ::ZeroMemory(&CmdData, sizeof(TAG_CONTROLCMDDATA));            iRc = recvEx(m_sClient, (char*)&CmdData, sizeof(TAG_CONTROLCMDDATA), 0);            if (SOCKET_ERROR == iRc) {                if (iErrSnPrev != WSAGetLastError()) {                    iErrSnPrev = WSAGetLastError();                    ShowSocketErrMsg();                }            }            else if (iRc > 0){                iErrSnPrev = WSAGetLastError();                ThreadProc_Recv(&CmdData);            }        }    } while (1);    return S_OK;}unsigned CClientDlg::ThreadProc_Recv(TAG_CONTROLCMDDATA* pCmdData) {    if (NULL != pCmdData) {        switch (pCmdData->Cmd) {        case eControlCmd_loginByClient:            ThreadProc_Recv_loginByClient(pCmdData);            break;        case eControlCmd_loginOutByClient:            ThreadProc_Recv_loginOutByClient(pCmdData);            break;        case eControlCmd_ImPublic:            ThreadProc_Recv_ImPublic(pCmdData);            break;        case eControlCmd_ImPrivate:            ThreadProc_Recv_ImPrivate(pCmdData);            break;        case eControlCmd_OnLine:            ThreadProc_Recv_UserOnline(pCmdData);            break;        case eControlCmd_UserList:            ThreadProc_Recv_UserList(pCmdData);            break;        case eControlCmd_DownLine:            ThreadProc_Recv_UserDownline(pCmdData);            break;        case eControlCmd_HeartBeat:            ThreadProc_Recv_HeartBeat(pCmdData);            break;        default:            break;        }    }    return S_OK;}void CClientDlg::OnShowThreadMsg() {    CString str;    TAG_CONTROLCMDDATA_USER_ONLINE UserOnlineInfo;    TAG_CONTROLCMDDATA_USER_DOWNLINE UserDownlineInfo;    EnterCriticalSection(&g_csDispMsg);    while (!m_ListDispMsg.empty()) {        str = m_ListDispMsg.front();        m_ListDispMsg.pop_front();        ShowMsg((LPTSTR)(LPCTSTR)str);    }    while (!m_ListUsersOnlineUpdate.empty()) {        UserOnlineInfo = m_ListUsersOnlineUpdate.front();        m_ListUsersOnlineUpdate.pop_front();        AddUserToList(            UserOnlineInfo.dwUserID,            UserOnlineInfo.szUserName);    }    while (!m_ListUsersDownlineUpdate.empty()) {        UserDownlineInfo = m_ListUsersDownlineUpdate.front();        m_ListUsersDownlineUpdate.pop_front();        RemoveUserFromList(            UserDownlineInfo.dwUserID,            UserDownlineInfo.szUserName);    }    LeaveCriticalSection(&g_csDispMsg);    /// 需要更新UI的操作也通过这个自定义消息    if (m_bNeedUpdateLoginInfo) {        m_bNeedUpdateLoginInfo = FALSE;        if (m_LoginInfoFromServer.IsLoginOk()) {            m_strMyIDFromServer.Format(_T("当前用户 : ID[%d] 姓名[%s]"),                 m_LoginInfoFromServer.GetUserID(),                m_LoginInfoFromServer.GetUserName());        } else {            m_strMyIDFromServer = _T("未登录用户");        }        this->SetWindowText((LPTSTR)(LPCTSTR)m_strMyIDFromServer);    }}void CClientDlg::ShowThreadMsg(TCHAR* pMsg) {    if (NULL != pMsg) {        EnterCriticalSection(&g_csDispMsg);        m_ListDispMsg.push_back(pMsg);        LeaveCriticalSection(&g_csDispMsg);        PostMessage(WM_SHOW_THREAD_MSG, 0, 0);    }}unsigned CClientDlg::ThreadProc_Recv_loginByClient(TAG_CONTROLCMDDATA* pCmdData) {    CString str;    if (NULL != pCmdData) {        m_LoginInfoFromServer = pCmdData->Data.LoginInfo;        str.Format(_T("登录结果: [%d]-[%s] 登录%s, 备注:%s"),            m_LoginInfoFromServer.GetUserID(),            m_LoginInfoFromServer.GetUserName(),            m_LoginInfoFromServer.IsLoginOk() ? _T("成功") : _T("失败"),            m_LoginInfoFromServer.szDesc);        ShowThreadMsg((LPTSTR)(LPCTSTR)str);        m_bNeedUpdateLoginInfo = TRUE;    }    return S_OK;}unsigned CClientDlg::ThreadProc_Recv_loginOutByClient(TAG_CONTROLCMDDATA* pCmdData) {    CString str;    if (NULL != pCmdData) {        m_LoginInfoFromServer = pCmdData->Data.LoginInfo;        str.Format(_T("登出结果: [%d]-[%s] 登出%s, 备注:%s"),            m_LoginInfoFromServer.GetUserID(),            m_LoginInfoFromServer.GetUserName(),            !m_LoginInfoFromServer.IsLoginOk() ? _T("成功") : _T("失败"),            m_LoginInfoFromServer.szDesc);        ShowThreadMsg((LPTSTR)(LPCTSTR)str);        m_bNeedUpdateLoginInfo = TRUE;        ConnectClose();    }    return S_OK;}unsigned CClientDlg::ThreadProc_Recv_UserOnline(TAG_CONTROLCMDDATA* pCmdData) {    if (NULL != pCmdData) {        EnterCriticalSection(&g_csDispMsg);        m_ListUsersOnlineUpdate.push_back(pCmdData->Data.UserOnline);        LeaveCriticalSection(&g_csDispMsg);        PostMessage(WM_SHOW_THREAD_MSG, 0, 0);    }    return S_OK;}unsigned CClientDlg::ThreadProc_Recv_UserList(TAG_CONTROLCMDDATA* pCmdData) {    CString str;    BOOL bNeedReadEx = FALSE; ///< 需要读扩展数据, TAG_CONTROLCMDDATA 装不下    DWORD cbNeedReadEx = 0; ///< 需要读多少扩展数据    BOOL bReadExOk = TRUE; ///< 读扩展数据是否成功    DWORD dwSizeToSend = sizeof(TAG_CONTROLCMDDATA);    DWORD dwSizeUserList = 0;    int iUserOnlineCnt = 0;    int iRc = 0;    TAG_CONTROLCMDDATA* pDst = NULL;    int i = 0;    TAG_CONTROLCMDDATA_USER_ONLINE* pUserInfoAry = NULL;    if (NULL == pCmdData) {        return FALSE;    }    iUserOnlineCnt = pCmdData->Data.UserInfoList.dwUserOnlineCnt;//    str.Format(_T("ThreadProc_Recv_UserList iUserOnlineCnt = %d"), iUserOnlineCnt);//    ShowThreadMsg((LPTSTR)(LPCSTR)str);    if (iUserOnlineCnt > 1) {        dwSizeUserList =             sizeof(eControlCmd)             + sizeof(TAG_CONTROLCMDDATA_USER_USERINFOLIST)             + sizeof(TAG_CONTROLCMDDATA_USER_ONLINE) * (iUserOnlineCnt - 1);        if (dwSizeToSend < dwSizeUserList) {            dwSizeToSend = dwSizeUserList;            bNeedReadEx = TRUE;        }    }    pDst = (TAG_CONTROLCMDDATA*)(new BYTE[dwSizeToSend]);    assert(NULL != pDst);    memcpy(pDst, pCmdData, sizeof(TAG_CONTROLCMDDATA));    if (bNeedReadEx) {        cbNeedReadEx = dwSizeToSend - sizeof(TAG_CONTROLCMDDATA);        iRc = recvEx(m_sClient, (char*)pDst + sizeof(TAG_CONTROLCMDDATA), cbNeedReadEx, 0);        if ((SOCKET_ERROR == iRc) || (0 == iRc)) {            ShowSocketErrMsg();            bReadExOk = FALSE;        }    }    if (bReadExOk) {        EnterCriticalSection(&g_csDispMsg);        for (i = 0; i < iUserOnlineCnt; i++) {            m_ListUsersOnlineUpdate.push_back(pDst->Data.UserInfoList.UserInfoAry[i]);        }        LeaveCriticalSection(&g_csDispMsg);        PostMessage(WM_SHOW_THREAD_MSG, 0, 0);    }    delete pDst;    pDst = NULL;    return S_OK;}unsigned CClientDlg::ThreadProc_Recv_UserDownline(TAG_CONTROLCMDDATA* pCmdData) {    CString str;    if (NULL != pCmdData) {//        str.Format(_T("ThreadProc_Recv_UserDownline pCmdData->Data.UserDownline.szUserName = %s"), //            pCmdData->Data.UserDownline.szUserName);//        ShowThreadMsg((LPTSTR)(LPCSTR)str);        EnterCriticalSection(&g_csDispMsg);        m_ListUsersDownlineUpdate.push_back(pCmdData->Data.UserDownline);        LeaveCriticalSection(&g_csDispMsg);        PostMessage(WM_SHOW_THREAD_MSG, 0, 0);    }    return S_OK;}unsigned CClientDlg::ThreadProc_Recv_ImPublic(TAG_CONTROLCMDDATA* pCmdData) {    CString str;    if (NULL != pCmdData) {        str.Format(_T("[%d][%s] : %s"),            pCmdData->Data.ImPublic.dwUserID,            pCmdData->Data.ImPublic.szUserName,            pCmdData->Data.ImPublic.szMsg);        ShowThreadMsg((LPTSTR)(LPCTSTR)str);    }    return S_OK;}unsigned CClientDlg::ThreadProc_Recv_ImPrivate(TAG_CONTROLCMDDATA* pCmdData) {    CString str;    if (NULL != pCmdData) {        str.Format(_T("[%d][%s]=>[%d][%s] : %s"),            pCmdData->Data.ImPrivate.MyData.dwUserID,            pCmdData->Data.ImPrivate.MyData.szUserName,            pCmdData->Data.ImPrivate.dwOtherUserID,            pCmdData->Data.ImPrivate.szOtherUserName,            pCmdData->Data.ImPrivate.MyData.szMsg);        ShowThreadMsg((LPTSTR)(LPCTSTR)str);    }    return S_OK;}unsigned CClientDlg::ThreadProc_Recv_HeartBeat(TAG_CONTROLCMDDATA* pCmdData) {    if (NULL != pCmdData) {    }    return S_OK;}void CClientDlg::AddDataToList(list<TAG_CONTROLCMDDATA*>& List, TAG_CONTROLCMDDATA* p, CRITICAL_SECTION& cs) {    if (NULL != p) {        EnterCriticalSection(&cs);        List.push_back(p);        LeaveCriticalSection(&cs);    }}TAG_CONTROLCMDDATA* CClientDlg::GetDataFromList(list<TAG_CONTROLCMDDATA*>& List, CRITICAL_SECTION& cs) {    TAG_CONTROLCMDDATA* p = NULL;    EnterCriticalSection(&cs);    if (!List.empty()) {        p = List.front();        List.pop_front();    }    LeaveCriticalSection(&cs);    return p;}void CClientDlg::RemoveList(list<TAG_CONTROLCMDDATA*>& List, CRITICAL_SECTION& cs) {    TAG_CONTROLCMDDATA* p = NULL;    list<TAG_CONTROLCMDDATA*>::iterator it;    EnterCriticalSection(&cs);    while (!List.empty()) {        p = List.front();        List.pop_front();        if (NULL != p) {            delete p;        }    }    LeaveCriticalSection(&cs);}void CClientDlg::OnDestroy() {    CDialog::OnDestroy();    ConnectClose();    m_ThreadSend.Stop();    m_ThreadRecv.Stop();    RemoveList(m_ListCmdDataToSend, g_csListCmdDataToSend);}void CClientDlg::OnButtonClearContent() {    UpdateData(TRUE);    m_csImMsg.Empty();    UpdateData(FALSE);}void CClientDlg::OnItemchangedListUsersOnline(NMHDR* pNMHDR, LRESULT* pResult) {    NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;    FillOtherUserIDFromList();    *pResult = 0;}void CClientDlg::OnClickListUsersOnline(NMHDR* pNMHDR, LRESULT* pResult) {    FillOtherUserIDFromList();    *pResult = 0;}void CClientDlg::FillOtherUserIDFromList() {    int nItem = 0;    DWORD dwUserID = 0;    POSITION pos = m_ListUsersOnline.GetFirstSelectedItemPosition();    if (NULL != pos) {        nItem = m_ListUsersOnline.GetNextSelectedItem(pos);        dwUserID = m_ListUsersOnline.GetItemData(nItem);        UpdateData(TRUE);        m_strOtherUserID.Format(_T("%d"), dwUserID);        UpdateData(FALSE);    }}

服务端

// SocketIM.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <windows.h>#include <Winsock2.h>#pragma comment(lib, "Ws2_32.lib")#include <tchar.h>#include <list>using namespace std;#include <assert.h>#include <time.h>#include "MyThread.h"#include "SocketRecvData.h"#include "ControlCmd.h"#include "SocketOpt.h"#define USE_SERVER_IP FALSE ///< TRUE, use SERVER_IP; FALSE, use SERVER_NAME#define SERVER_NAME _T("localhost")#define SERVER_IP 0x7f000001 ///< 127.0.0.1 0x7f000001#define SERVER_PORT 12345#define MAX_CLIENT_CNT 999CMyThread m_ThreadSend; ///< 发送线程list<TAG_CONTROLCMDDATA*> g_ListThreadSend; ///< 发送线程的DataListCRITICAL_SECTION g_csListThreadSend;/// 心跳检查不要放在发送线程中兼职, 会引起死锁/// 心跳检查必须是一条单独的线程CMyThread m_ThreadHeartBeat; ///< 心跳检查线程list<CMyThread*> g_ListThreadRecv; ///< 用于接收Client数据的线程列表CRITICAL_SECTION g_csListThreadRecv;void AddDataToList(list<TAG_CONTROLCMDDATA*>& List, TAG_CONTROLCMDDATA* p, CRITICAL_SECTION& cs);TAG_CONTROLCMDDATA* GetDataFromList(list<TAG_CONTROLCMDDATA*>& List, CRITICAL_SECTION& cs);void RemoveList(list<TAG_CONTROLCMDDATA*>& List, CRITICAL_SECTION& cs);void AddThreadToListThreadRecv(CMyThread* p);void StopAndRemoveAllFromListThreadRecv();BOOL RemoveTrashFromListThreadRecv();VOID EnterCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, TCHAR* pTip);VOID LeaveCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, TCHAR* pTip);void SocketServerProc();void SocketConnectProc(SOCKET sServer); ///< 处理客户端连接void ShowSocketErrMsg();void ShowErrMsg();void ShowErrMsg(int iErrSn);void ShowMsg(TCHAR* pMsg);BOOL FindExistUser(TAG_CONTROLCMDDATA_LOGINBYCLIENT* pLoginInfo);BOOL PrintfInfo_ListThreadRecv();BOOL ProcessUserLogin(CSocketRecvData* pData, TAG_CONTROLCMDDATA_LOGINBYCLIENT& LoginInfoByClient);BOOL ProcessUserLoginOut(CSocketRecvData* pData, TAG_CONTROLCMDDATA_LOGINBYCLIENT& LoginInfo);BOOL ProcessIm(CSocketRecvData* pData, TAG_CONTROLCMDDATA& CmdData);BOOL ProcessIm_PublicTalk(CSocketRecvData* pData, TAG_CONTROLCMDDATA* pCmdData);BOOL ProcessIm_PrivateTalk(CSocketRecvData* pData, TAG_CONTROLCMDDATA* pCmdData);BOOL ProcessIm_HeartBeat(CSocketRecvData* pData, TAG_CONTROLCMDDATA* pCmdData);BOOL Notify_UserOnLine(TAG_CONTROLCMDDATA* p);BOOL Notify_UpdateListUserOnLine(TAG_CONTROLCMDDATA* p);BOOL Notify_UserDownLine(TAG_CONTROLCMDDATA* p);unsigned fnThreadProc_SocketSend_ImPublic(TAG_CONTROLCMDDATA* p);unsigned fnThreadProc_SocketSend_loginByClient(TAG_CONTROLCMDDATA* p);unsigned fnThreadProc_SocketSend_loginOutByClient(TAG_CONTROLCMDDATA* p);unsigned fnThreadProc_SocketSend_ImPrivate(TAG_CONTROLCMDDATA* p);unsigned fnThreadProc_SocketSend_UserOnline(TAG_CONTROLCMDDATA* p);unsigned fnThreadProc_SocketSend_UpdateUserList(TAG_CONTROLCMDDATA* p);unsigned fnThreadProc_SocketSend_UserDownline(TAG_CONTROLCMDDATA* p);unsigned __stdcall fnThreadProc_SocketSend(void* pParam);unsigned __stdcall fnThreadProc_HeartBeatCheck(void* pParam);unsigned __stdcall fnThreadProc_SocketRecv(void* pParam);unsigned __stdcall fnStopThread_SocketRecv(void* pParam);unsigned __stdcall fnDetoryUserDataProc_SocketRecv(void* pParam);int main(int argc, char* argv[]){    WSADATA WSAData;    WSAStartup(MAKEWORD(2,2), &WSAData);    InitializeCriticalSection(&g_csListThreadSend);    InitializeCriticalSection(&g_csListThreadRecv);    ShowMsg(_T("------------------------------------------------------------"));    ShowMsg(_T("|                        IM Server                         |"));    ShowMsg(_T("------------------------------------------------------------"));    SocketServerProc();    m_ThreadHeartBeat.Stop();    StopAndRemoveAllFromListThreadRecv();    m_ThreadSend.Stop();    RemoveList(g_ListThreadSend, g_csListThreadSend);    ShowMsg(_T("IM Server END\n"));    DeleteCriticalSection(&g_csListThreadRecv);    DeleteCriticalSection(&g_csListThreadSend);    WSACleanup();    return 0;}void SocketServerProc() {    SOCKET sServer = INVALID_SOCKET;    struct sockaddr_in addrServer;    struct hostent* pHost = NULL;    int iRc = 0;    TCHAR cBufToDisp[MAXBYTE] = {_T('\0')};    do {        sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);        if (INVALID_SOCKET == sServer) {            ShowSocketErrMsg();            break;        }        addrServer.sin_family = AF_INET;        addrServer.sin_port = htons(SERVER_PORT);        if (USE_SERVER_IP) {            addrServer.sin_addr.S_un.S_addr = htonl(SERVER_IP);        } else {            pHost = gethostbyname(SERVER_NAME);            if (NULL == pHost)            {                ShowSocketErrMsg();                break;            }            CopyMemory(&addrServer.sin_addr, pHost->h_addr_list[0], pHost->h_length);        }        iRc = bind(sServer, (SOCKADDR*)&addrServer, sizeof(addrServer));        if (SOCKET_ERROR == iRc) {            ShowSocketErrMsg();            break;        }        iRc = listen(sServer, MAX_CLIENT_CNT);        if (SOCKET_ERROR == iRc) {            ShowSocketErrMsg();            break;        }        m_ThreadSend.SetParam(            fnThreadProc_SocketSend,            NULL,            NULL,            NULL);        m_ThreadSend.Start();        m_ThreadHeartBeat.SetParam(            fnThreadProc_HeartBeatCheck,            NULL,            NULL,            NULL);        m_ThreadHeartBeat.Start();        _stprintf(cBufToDisp, _T("Server : %s - %d.%d.%d.%d:%d"),             SERVER_NAME,            (SERVER_IP >> 24) & 0xff,            (SERVER_IP >> 16) & 0xff,            (SERVER_IP >> 8) & 0xff,            (SERVER_IP >> 0) & 0xff,            SERVER_PORT);        ShowMsg(cBufToDisp);        do {            SocketConnectProc(sServer);        } while (1);    } while (0);}void SocketConnectProc(SOCKET sServer) {    sockaddr_in addr;    SOCKET sClient = SOCKET_ERROR;    int nSize = sizeof(addr);    CMyThread* pThread = NULL;    TCHAR cBufToDisp[MAXBYTE] = {_T('\0')};    do {        ShowMsg(_T("Waiting Client Connect..."));        sClient = accept(sServer, (SOCKADDR*)&addr, &nSize);        if (SOCKET_ERROR == sClient) {            ShowSocketErrMsg();            break;        }        _stprintf(cBufToDisp, _T("Client[%d] Was Connect"), sClient);        ShowMsg(cBufToDisp);        /// 开始新线程        pThread = new CMyThread;        pThread->SetParam(            fnThreadProc_SocketRecv,            fnStopThread_SocketRecv,            fnDetoryUserDataProc_SocketRecv,            new CSocketRecvData());        ((CSocketRecvData*)pThread->GetUserData())->SetSock(sClient);        ((CSocketRecvData*)pThread->GetUserData())->SetThreadClass(pThread);        AddThreadToListThreadRecv(pThread);        pThread->Start();    } while (0);}BOOL ProcessUserLogin(CSocketRecvData* pData, TAG_CONTROLCMDDATA_LOGINBYCLIENT& LoginInfoByClient) {    int iRc = 0;    TAG_CONTROLCMDDATA* pCmdData = NULL;    TCHAR cBufToDisp[4096] = {_T('\0')};    CMyThread* pThread = NULL;    assert(NULL != pData);    /// 认证    LoginInfoByClient.dwUserID = pData->GetSock();    if (LoginInfoByClient.IsValidLoginData()) {        /// 查找是否该用户已经登录?        if (FindExistUser(&LoginInfoByClient)) {            LoginInfoByClient.bLoginOK = FALSE;            _tcscpy(LoginInfoByClient.szDesc, _T("该用户已经登录, 请使用新的身份重新登录"));        } else {            LoginInfoByClient.bLoginOK = TRUE;            _tcscpy(LoginInfoByClient.szDesc, _T("登录成功"));        }    }    pData->GetLoginInfo() = LoginInfoByClient;    _stprintf(cBufToDisp, _T("sClient[%d] : %s, UserName = %s, Pwd = %s"),        LoginInfoByClient.GetUserID(),         LoginInfoByClient.szDesc,        LoginInfoByClient.szUserName,        LoginInfoByClient.szPwd);    ShowMsg(cBufToDisp);    pCmdData = new TAG_CONTROLCMDDATA;    if (NULL != pCmdData) {        pCmdData->Cmd = eControlCmd_loginByClient;        pCmdData->Data.LoginInfo = LoginInfoByClient;        /// 将认证结果发给客户端(先发!, 否则客户端用户在线状态加不上)        /// 只有用户先登录了, 才会有好友列表        AddDataToList(g_ListThreadSend, pCmdData, g_csListThreadSend);        if (LoginInfoByClient.IsLoginOk()) {            /// 通知其它用户, 此用户上线            Notify_UserOnLine(pCmdData);            /// 该用户更新用户列表            Notify_UpdateListUserOnLine(pCmdData);        }    }    return LoginInfoByClient.bLoginOK;}BOOL Notify_UserDownLine(TAG_CONTROLCMDDATA* p) {    TAG_CONTROLCMDDATA* pCmdData = NULL;    if ((NULL == p) || (eControlCmd_loginOutByClient != p->Cmd)) {        return FALSE;    }    pCmdData = new TAG_CONTROLCMDDATA;    if (NULL != pCmdData) {        pCmdData->Cmd = eControlCmd_DownLine;        pCmdData->Data.UserDownline.dwUserID = p->Data.LoginInfo.GetUserID();        _tcscpy(pCmdData->Data.UserDownline.szUserName, p->Data.LoginInfo.GetUserName());        AddDataToList(g_ListThreadSend, pCmdData, g_csListThreadSend);    }    return TRUE;}BOOL Notify_UserOnLine(TAG_CONTROLCMDDATA* p) {    TAG_CONTROLCMDDATA* pCmdData = NULL;    if ((NULL == p) || (eControlCmd_loginByClient != p->Cmd)) {        return FALSE;    }    pCmdData = new TAG_CONTROLCMDDATA;    if (NULL != pCmdData) {        pCmdData->Cmd = eControlCmd_OnLine;        pCmdData->Data.UserOnline.dwUserID = p->Data.LoginInfo.GetUserID();        _tcscpy(pCmdData->Data.UserOnline.szUserName, p->Data.LoginInfo.GetUserName());        AddDataToList(g_ListThreadSend, pCmdData, g_csListThreadSend);    }    return TRUE;}BOOL Notify_UpdateListUserOnLine(TAG_CONTROLCMDDATA* p) {    TAG_CONTROLCMDDATA* pCmdData = NULL;    if ((NULL == p) || (eControlCmd_loginByClient != p->Cmd)) {        return FALSE;    }    pCmdData = new TAG_CONTROLCMDDATA;    if (NULL != pCmdData) {        pCmdData->Cmd = eControlCmd_UserList;        pCmdData->Data.UserInfoList.dwUserID = p->Data.LoginInfo.GetUserID();        _tcscpy(pCmdData->Data.UserInfoList.szUserName, p->Data.LoginInfo.GetUserName());        AddDataToList(g_ListThreadSend, pCmdData, g_csListThreadSend);    }    return TRUE;}BOOL ProcessUserLoginOut(CSocketRecvData* pData, TAG_CONTROLCMDDATA_LOGINBYCLIENT& LoginInfo) {    TCHAR cBufToDisp[4096] = {_T('\0')};    TAG_CONTROLCMDDATA CmdData;    TAG_CONTROLCMDDATA* pCmdData = NULL;    assert(NULL != pData);    LoginInfo.bLoginOK = FALSE;    _tcscpy(LoginInfo.szDesc, _T("用户要求退出登录"));    CmdData.Data.LoginInfo = LoginInfo;    CmdData.Cmd = eControlCmd_loginOutByClient;    _stprintf(cBufToDisp, _T("sClient[%d] : %s, UserName = %s, Pwd = %s"),        pData->GetSock(),         LoginInfo.szDesc,        LoginInfo.szUserName,        LoginInfo.szPwd);    ShowMsg(cBufToDisp);    /// 将认证结果发给客户端    pCmdData = new TAG_CONTROLCMDDATA;    if (NULL != pCmdData) {        memcpy(pCmdData, &CmdData, sizeof(TAG_CONTROLCMDDATA));        AddDataToList(g_ListThreadSend, pCmdData, g_csListThreadSend);    }    /// 遍历用户列表, 通知此用户下线    Notify_UserDownLine(&CmdData);    return TRUE;}BOOL FindExistUser(TAG_CONTROLCMDDATA_LOGINBYCLIENT* pLoginInfo) {    BOOL bRc = FALSE;    list<CMyThread*>::iterator it;    CSocketRecvData* pData = NULL;    if (NULL == pLoginInfo) {        return FALSE;    }    EnterCriticalSectionEx(&g_csListThreadRecv, _T("1.0"));    for (it = g_ListThreadRecv.begin(); it != g_ListThreadRecv.end(); it++) {        if (NULL != *it) {            if (!(*it)->IsRunning()) {                continue;            }            pData = (CSocketRecvData*)(*it)->GetUserData();            if (NULL != pData) {                if (0 == _tcscmp(pData->GetLoginInfo().szUserName, pLoginInfo->szUserName)) {                    bRc = TRUE;                    break;                }            }        }    }    LeaveCriticalSectionEx(&g_csListThreadRecv, _T("1.1"));    return bRc;}BOOL PrintfInfo_ListThreadRecv() {    BOOL bRc = FALSE;    list<CMyThread*>::iterator it;    CSocketRecvData* pData = NULL;    EnterCriticalSectionEx(&g_csListThreadRecv, _T("2.0"));    for (it = g_ListThreadRecv.begin(); it != g_ListThreadRecv.end(); it++) {        if (NULL != *it) {            pData = (CSocketRecvData*)(*it)->GetUserData();            if (NULL != pData) {                pData->GetLoginInfo().ShowDetail();            }        }    }    LeaveCriticalSectionEx(&g_csListThreadRecv, _T("2.1"));    return bRc;}BOOL ProcessIm(CSocketRecvData* pData, TAG_CONTROLCMDDATA& CmdData) {    /// 接收登录数据    int iRc = 0;    iRc = recvEx(pData->GetSock(), (char*)&CmdData, sizeof(CmdData), 0);    if (SOCKET_ERROR == iRc) {        ShowSocketErrMsg();        return FALSE;    }    if (iRc != sizeof(CmdData)) {        return FALSE;    }    return TRUE;}BOOL ProcessIm_PublicTalk(CSocketRecvData* pData, TAG_CONTROLCMDDATA* pCmdData) {    TAG_CONTROLCMDDATA* pCmdDataDst = NULL;//    ShowMsg(_T("收到群聊数据"));    if (NULL != pCmdData) {        pCmdDataDst = new TAG_CONTROLCMDDATA;        if (NULL != pCmdDataDst) {            memcpy(pCmdDataDst, pCmdData, sizeof(TAG_CONTROLCMDDATA));            AddDataToList(g_ListThreadSend, pCmdDataDst, g_csListThreadSend);        }    }    return TRUE;}BOOL ProcessIm_PrivateTalk(CSocketRecvData* pData, TAG_CONTROLCMDDATA* pCmdData) {    TAG_CONTROLCMDDATA* pCmdDataDst = NULL;//    ShowMsg(_T("收到私聊数据"));    if (NULL != pCmdData) {        pCmdDataDst = new TAG_CONTROLCMDDATA;        if (NULL != pCmdDataDst) {            memcpy(pCmdDataDst, pCmdData, sizeof(TAG_CONTROLCMDDATA));            AddDataToList(g_ListThreadSend, pCmdDataDst, g_csListThreadSend);        }    }    return TRUE;}BOOL ProcessIm_HeartBeat(CSocketRecvData* pData, TAG_CONTROLCMDDATA* pCmdData) {    CMyThread* pThread = NULL;    list<CMyThread*>::iterator it;    CSocketRecvData* pUserData = NULL;    TCHAR cBufToDisp[4096] = {_T('\0')};    SYSTEMTIME st;    if (NULL != pCmdData) {        /// 更新心跳时间戳        EnterCriticalSectionEx(&g_csListThreadRecv, _T("3.0"));        for (it = g_ListThreadRecv.begin(); it != g_ListThreadRecv.end();it++) {            pThread = *it;            if ((NULL != pThread) && pThread->IsRunning()) {                pUserData = (class CSocketRecvData *)pThread->GetUserData();                if (NULL == pUserData) {                    continue;                }                if (pUserData->GetLoginInfo().dwUserID == pCmdData->Data.ImHeartBeat.dwUserID) {                    /// 更新时间戳                    pUserData->UpdateHeartBeatTimestamp();                    st = pUserData->GetHeartBeatTimestampSystemTime();                    _stprintf(cBufToDisp, _T("收到[%d]心跳数据[%d], 时间戳[%d-%d-%d %d:%d:%d %d]"),                        pCmdData->Data.ImHeartBeat.dwUserID,                        pUserData->GetHeartBeatTimestampCnt(),                        st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);                    ShowMsg(cBufToDisp);                    break;                }            }        }        LeaveCriticalSectionEx(&g_csListThreadRecv, _T("3.1"));    }    return TRUE;}BOOL tickcount_2_systemtime(long lTickCount, SYSTEMTIME* pst){    BOOL        bRc = FALSE;    time_t      tmt = -1;    struct tm*  pGmt = NULL;    long lTmp = lTickCount;    do     {        if (NULL == pst)            break;        //pGmt = localtime(&lTmp);        tmt = lTickCount;        pGmt = gmtime(&tmt);        if (NULL == pGmt)            break;        pst->wYear = 1900 + pGmt->tm_year;        pst->wMonth = 1 + pGmt->tm_mon;        pst->wDay = pGmt->tm_mday;        pst->wHour = pGmt->tm_hour;        pst->wMinute = pGmt->tm_min;        pst->wSecond = pGmt->tm_sec;        pst->wDayOfWeek = pGmt->tm_wday;        bRc = TRUE;    } while (0);    return bRc;}unsigned fnThreadProc_SocketSend_UserOnline(TAG_CONTROLCMDDATA* p) {    int iRc = 0;    CMyThread* pThread = NULL;    list<CMyThread*>::iterator it;    CSocketRecvData* pUserData = NULL;    if (NULL == p) {        return S_FALSE;    }    /// 遍历当前用户接收线程列表, 将p发到每个用户    EnterCriticalSectionEx(&g_csListThreadRecv, _T("4.0"));    for (it = g_ListThreadRecv.begin(); it != g_ListThreadRecv.end();it++) {        pThread = *it;        if ((NULL != pThread) && pThread->IsRunning()) {            pUserData = (class CSocketRecvData *)pThread->GetUserData();            if (NULL == pUserData) {                continue;            }            if (pUserData->GetLoginInfo().dwUserID == p->Data.UserOnline.dwUserID) {                /// 不发给用户自己                continue;            }            iRc = sendEx(pUserData->GetLoginInfo().dwUserID, (const char *)p, sizeof(TAG_CONTROLCMDDATA), 0);        }    }    LeaveCriticalSectionEx(&g_csListThreadRecv, _T("4.1"));    iRc = 0;    return iRc;}unsigned fnThreadProc_SocketSend_UpdateUserList(TAG_CONTROLCMDDATA* p) {    int iUserOnlineCnt = 0;    int iIndex = 0;    DWORD dwSizeToSend = 0; ///< 最终发送需要的容量    DWORD dwSizeUserList = 0; ///< 用户列表信息需要的容量    int iRc = 0;    CMyThread* pThread = NULL;    list<CMyThread*>::iterator it;    CSocketRecvData* pUserData = NULL;    TAG_CONTROLCMDDATA* pDst = NULL;    if (NULL == p) {        return iRc;    }    /// 遍历当前用户接收线程列表, 统计在线用户数量(不包括自己)    EnterCriticalSectionEx(&g_csListThreadRecv, _T("5.0"));    for (it = g_ListThreadRecv.begin(); it != g_ListThreadRecv.end();it++) {        pThread = *it;        if ((NULL != pThread) && pThread->IsRunning()) {            pUserData = (class CSocketRecvData *)pThread->GetUserData();            if (NULL == pUserData) {                continue;            }            if (pUserData->GetLoginInfo().dwUserID == p->Data.UserOnline.dwUserID) {                /// 不发给用户自己                continue;            }            iUserOnlineCnt++;         }    }    /// 填充在线用户列表数据结构, 并发送给刚登录的用户    if (iUserOnlineCnt > 0) {        dwSizeUserList =             sizeof(eControlCmd)             + sizeof(TAG_CONTROLCMDDATA_USER_USERINFOLIST)             + sizeof(TAG_CONTROLCMDDATA_USER_ONLINE) * (iUserOnlineCnt - 1);        dwSizeToSend = sizeof(TAG_CONTROLCMDDATA);        if (dwSizeToSend < dwSizeUserList) {            dwSizeToSend = dwSizeUserList;        }        pDst = (TAG_CONTROLCMDDATA*)(new BYTE[dwSizeToSend]);        assert(NULL != pDst);        pDst->Cmd = eControlCmd_UserList;        pDst->Data.UserInfoList.dwUserID = p->Data.UserOnline.dwUserID;        _tcscpy(pDst->Data.UserInfoList.szUserName, p->Data.UserOnline.szUserName);        pDst->Data.UserInfoList.dwUserOnlineCnt = iUserOnlineCnt;        for (it = g_ListThreadRecv.begin(); it != g_ListThreadRecv.end();it++) {            pThread = *it;            if ((NULL != pThread) && pThread->IsRunning()) {                pUserData = (class CSocketRecvData *)pThread->GetUserData();                if (NULL == pUserData) {                    continue;                }                if (pUserData->GetLoginInfo().dwUserID == p->Data.UserOnline.dwUserID) {                    /// 用户自己的在线状态不发给用户自己                    continue;                }                pDst->Data.UserInfoList.UserInfoAry[iIndex].dwUserID = pUserData->GetLoginInfo().dwUserID;                _tcscpy(pDst->Data.UserInfoList.UserInfoAry[iIndex].szUserName, pUserData->GetLoginInfo().szUserName);                iIndex++;            }        }        iRc = sendEx(p->Data.UserOnline.dwUserID, (const char *)pDst, dwSizeToSend, 0);        delete pDst;        pDst = NULL;    }    LeaveCriticalSectionEx(&g_csListThreadRecv, _T("5.1"));    return iRc;}unsigned fnThreadProc_SocketSend_loginByClient(TAG_CONTROLCMDDATA* p) {    sendEx(p->Data.LoginInfo.dwUserID, (const char *)p, sizeof(TAG_CONTROLCMDDATA), 0);    return 0;}unsigned fnThreadProc_SocketSend_loginOutByClient(TAG_CONTROLCMDDATA* p) {    sendEx(p->Data.LoginInfo.dwUserID, (const char *)p, sizeof(TAG_CONTROLCMDDATA), 0);    return 0;}unsigned fnThreadProc_SocketSend_UserDownline(TAG_CONTROLCMDDATA* p) {    int iRc = 0;    CMyThread* pThread = NULL;    list<CMyThread*>::iterator it;    CSocketRecvData* pUserData = NULL;    if (NULL == p) {        return S_FALSE;    }    /// 遍历当前用户接收线程列表, 将p发到每个用户    EnterCriticalSectionEx(&g_csListThreadRecv, _T("6.0"));    for (it = g_ListThreadRecv.begin(); it != g_ListThreadRecv.end();it++) {        pThread = *it;        if ((NULL != pThread) && pThread->IsRunning()) {            pUserData = (class CSocketRecvData *)pThread->GetUserData();            if (NULL == pUserData) {                continue;            }            if (pUserData->GetLoginInfo().dwUserID == p->Data.UserDownline.dwUserID) {                /// 不发给用户自己                continue;            }            iRc = sendEx(pUserData->GetLoginInfo().dwUserID, (const char *)p, sizeof(TAG_CONTROLCMDDATA), 0);        }    }    LeaveCriticalSectionEx(&g_csListThreadRecv, _T("6.1"));    return S_OK;}unsigned fnThreadProc_SocketSend_ImPublic(TAG_CONTROLCMDDATA* p) {    int iRc = 0;    CMyThread* pThread = NULL;    list<CMyThread*>::iterator it;    CSocketRecvData* pUserData = NULL;    if (NULL == p) {        return S_FALSE;    }    /// 遍历当前用户接收线程列表, 将p发到每个用户    EnterCriticalSectionEx(&g_csListThreadRecv, _T("7.0"));    for (it = g_ListThreadRecv.begin(); it != g_ListThreadRecv.end();it++) {        pThread = *it;        if ((NULL != pThread) && pThread->IsRunning()) {            pUserData = (class CSocketRecvData *)pThread->GetUserData();            if (NULL != pUserData) {                iRc = sendEx(pUserData->GetLoginInfo().dwUserID, (const char *)p, sizeof(TAG_CONTROLCMDDATA), 0);            }        }    }    LeaveCriticalSectionEx(&g_csListThreadRecv, _T("7.1"));    return 0;}unsigned fnThreadProc_SocketSend_ImPrivate(TAG_CONTROLCMDDATA* p) {    int iRc = 0;    CMyThread* pThread = NULL;    list<CMyThread*>::iterator it;    CSocketRecvData* pUserData = NULL;    if (NULL == p) {        return iRc;    }    /// 遍历当前用户接收线程列表, 将p发到指定用户, 缺的参数填上    EnterCriticalSectionEx(&g_csListThreadRecv, _T("8.0"));    for (it = g_ListThreadRecv.begin(); it != g_ListThreadRecv.end();it++) {        pThread = *it;        if ((NULL != pThread) && pThread->IsRunning()) {            pUserData = (class CSocketRecvData *)pThread->GetUserData();            if (NULL != pUserData) {                if (pUserData->GetLoginInfo().GetUserID() == p->Data.ImPrivate.dwOtherUserID) {                    _tcscpy(p->Data.ImPrivate.szOtherUserName, pUserData->GetLoginInfo().GetUserName());                    iRc = sendEx(p->Data.ImPrivate.dwOtherUserID, (const char *)p, sizeof(TAG_CONTROLCMDDATA), 0);                    if (SOCKET_ERROR == iRc) {                        ShowSocketErrMsg();                    }                    break;                }            }        }    }    LeaveCriticalSectionEx(&g_csListThreadRecv, _T("8.1"));    return iRc;}unsigned __stdcall fnThreadProc_HeartBeatCheck(void* pParam) {    DWORD dwHeartBeatSpanTime = SERVER_HEARTBEAT_CHECK_SPANTIME;    DWORD dwHeartBeatTimeBegin = GetTickCount();    ShowMsg(_T(">> fnThreadProc_HeartBeatCheck"));    do {        Sleep(1);        if (!m_ThreadHeartBeat.cbIsCanContinue()) {            continue;        }        if (m_ThreadHeartBeat.cbIsNeedQuit()) {            break;        }        if ((GetTickCount() - dwHeartBeatTimeBegin) >= dwHeartBeatSpanTime) {            dwHeartBeatTimeBegin = GetTickCount();            /// 心跳发送间隔到, 开始清理垃圾接收线程, 更新客户端在线用户列表            RemoveTrashFromListThreadRecv();        }    } while (1);    ShowMsg(_T("<< fnThreadProc_HeartBeatCheck"));    return 0;}unsigned __stdcall fnThreadProc_SocketSend(void* pParam) {    int iRc = 0;    TAG_CONTROLCMDDATA* p = NULL;    ShowMsg(_T(">> fnThreadProc_SocketSend"));    do {        Sleep(1);        if (!m_ThreadSend.cbIsCanContinue()) {            continue;        }        if (m_ThreadSend.cbIsNeedQuit()) {            break;        }        p = GetDataFromList(g_ListThreadSend, g_csListThreadSend);        if (NULL == p) {            continue;        }        // _tprintf(_T("p->Cmd = %d\n"), p->Cmd);        switch (p->Cmd) {        case eControlCmd_loginByClient:            iRc = fnThreadProc_SocketSend_loginByClient(p);            break;        case eControlCmd_loginOutByClient:            iRc = fnThreadProc_SocketSend_loginOutByClient(p);            break;        case eControlCmd_ImPublic:            iRc = fnThreadProc_SocketSend_ImPublic(p);            break;        case eControlCmd_ImPrivate:            iRc = fnThreadProc_SocketSend_ImPrivate(p);            break;        case eControlCmd_OnLine:            iRc = fnThreadProc_SocketSend_UserOnline(p);            break;        case eControlCmd_UserList:            iRc = fnThreadProc_SocketSend_UpdateUserList(p);            break;        case eControlCmd_DownLine:            iRc = fnThreadProc_SocketSend_UserDownline(p);            break;        case eControlCmd_HeartBeat:            break;        default:            break;        }        if (NULL != p) {            delete p;            p = NULL;        }        if (SOCKET_ERROR == iRc) {            ShowSocketErrMsg();            break;        }    } while (1);    ShowMsg(_T("<< fnThreadProc_SocketSend"));    return 0;}unsigned __stdcall fnThreadProc_SocketRecv(void* pParam) {    TCHAR cBufToDisp[4096] = {_T('\0')};    CSocketRecvData* pData = (CSocketRecvData*)pParam;    TAG_CONTROLCMDDATA CmdData;    BOOL bNeedQuit = FALSE;    if (NULL == pData) {        return 0;    }    do {        if (NULL != pData->GetThreadClass()) {            if (!pData->GetThreadClass()->cbIsCanContinue()) {                continue;            }            if (pData->GetThreadClass()->cbIsNeedQuit()) {                break;            }        }        /// 接收认证成功后的数据(群聊,私聊,心跳)        if (ProcessIm(pData, CmdData)) {            switch (CmdData.Cmd) {            case eControlCmd_loginByClient:                {                    ProcessUserLogin(pData, CmdData.Data.LoginInfo);                }                break;            case eControlCmd_loginOutByClient:                {                    ProcessUserLoginOut(pData, pData->GetLoginInfo());                    bNeedQuit = TRUE;                }                break;            case eControlCmd_ImPublic:                {                    ProcessIm_PublicTalk(pData, &CmdData);                }                break;            case eControlCmd_ImPrivate:                {                    ProcessIm_PrivateTalk(pData, &CmdData);                }                break;            case eControlCmd_HeartBeat:                {                    ProcessIm_HeartBeat(pData, &CmdData);                }                break;            default:                ShowMsg(_T("收到未知的协议格式, 是否需要更新服务端?"));                break;            }        } else {            bNeedQuit = TRUE;        }    } while (!bNeedQuit);    _stprintf(cBufToDisp, _T("<< fnThreadProc_SocketRecv[%d]"), pData->GetSock());    ShowMsg(cBufToDisp);    return 0;}unsigned __stdcall fnStopThread_SocketRecv(void* pParam) {    CSocketRecvData* pData = (CSocketRecvData*)pParam;    if (NULL != pData) {        /// 关掉socket句柄, socket句柄相关的阻塞操作,就失败返回了        if ((NULL != pData->GetSock()) && (SOCKET_ERROR != pData->GetSock())) {            closesocket(pData->GetSock());        }    }    return 0;}unsigned __stdcall fnDetoryUserDataProc_SocketRecv(void* pParam) {    CSocketRecvData* pData = (CSocketRecvData*)pParam;    if (NULL != pData) {        delete pData;    }    return 0;}void ShowSocketErrMsg() {    int iErrSn = WSAGetLastError();    ShowErrMsg(iErrSn);}void ShowErrMsg() {    int iErrSn = GetLastError();    ShowErrMsg(iErrSn);}void ShowErrMsg(int iErrSn) {    LPVOID lpMsgBuf = NULL;    FormatMessage(         FORMAT_MESSAGE_ALLOCATE_BUFFER |         FORMAT_MESSAGE_FROM_SYSTEM |         FORMAT_MESSAGE_IGNORE_INSERTS,        NULL,        iErrSn,        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language        (LPTSTR) &lpMsgBuf,        0,        NULL);    if (NULL != lpMsgBuf) {        ShowMsg((TCHAR*)lpMsgBuf);    }    LocalFree(lpMsgBuf);}void ShowMsg(TCHAR* pMsg) {    if (NULL != pMsg) {        _tprintf(_T("%s\r\n"), pMsg);    }}void AddThreadToListThreadRecv(CMyThread* p) {    if (NULL != p) {        EnterCriticalSectionEx(&g_csListThreadRecv, _T("9.0"));        g_ListThreadRecv.push_back(p);        LeaveCriticalSectionEx(&g_csListThreadRecv, _T("9.1"));    }}void StopAndRemoveAllFromListThreadRecv() {    list<CMyThread*>::iterator it;    CMyThread* pThread = NULL;    EnterCriticalSectionEx(&g_csListThreadRecv, _T("10.0"));    while (!g_ListThreadRecv.empty()) {        it = g_ListThreadRecv.begin();        pThread = *it;                    if (NULL != pThread) {            g_ListThreadRecv.erase(it);            pThread->Stop();            delete pThread;            pThread = NULL;        }    }    LeaveCriticalSectionEx(&g_csListThreadRecv, _T("10.1"));}BOOL RemoveTrashFromListThreadRecv() {    BOOL bHeartBeatTimeOut = FALSE;    CMyThread* pThread = NULL;    CSocketRecvData* pUserData = NULL;    list<CMyThread*>::iterator it;    TAG_CONTROLCMDDATA* pCmdData = NULL;    TCHAR cBufToDisp[4096] = {_T('\0')};    EnterCriticalSectionEx(&g_csListThreadRecv, _T("11.0"));    for (it = g_ListThreadRecv.begin(); it != g_ListThreadRecv.end();) {        pThread = *it;        if (NULL != pThread) {            pUserData = (CSocketRecvData*)pThread->GetUserData();            if (NULL == pUserData) {                continue;            }            if (!pThread->IsRunning()) {                /// 压入客户端下线数据                if (NULL != pUserData) {                    pCmdData = new TAG_CONTROLCMDDATA;                    if (NULL != pCmdData) {                        _stprintf(cBufToDisp, _T("HeartBeat : UserDownline[%d]"), pUserData->GetLoginInfo().GetUserID());                        ShowMsg(cBufToDisp);                        pCmdData->Cmd = eControlCmd_DownLine;                        pCmdData->Data.UserDownline.dwUserID = pUserData->GetLoginInfo().GetUserID();                        _tcscpy(pCmdData->Data.UserDownline.szUserName, pUserData->GetLoginInfo().GetUserName());                        AddDataToList(g_ListThreadSend, pCmdData, g_csListThreadSend);                    }                }                /// 只能直接删已经停止的线程, 防止要删除的线程中用到g_csListThreadRecv死锁                it = g_ListThreadRecv.erase(it);                pThread->Stop();                delete pThread;                pThread = NULL;            } else {                bHeartBeatTimeOut = ((GetTickCount() - pUserData->GetTickHeartBeatTimestamp()) > SERVER_HEARTBEAT_CHECK_SPANTIME);                 if (bHeartBeatTimeOut) {                    /// 对于超时的接收线程, 关掉Sockt句柄, 让她由于错误而自然退出.                    closesocket(pUserData->GetSock());                    pUserData->SetSock(INVALID_SOCKET);                }                it++;            }        }    }    LeaveCriticalSectionEx(&g_csListThreadRecv, _T("11.1"));    return TRUE;}void AddDataToList(list<TAG_CONTROLCMDDATA*>& List, TAG_CONTROLCMDDATA* p, CRITICAL_SECTION& cs) {    if (NULL != p) {        EnterCriticalSectionEx(&cs, _T("AddDataToList.0"));        List.push_back(p);        LeaveCriticalSectionEx(&cs, _T("AddDataToList.1"));    }}TAG_CONTROLCMDDATA* GetDataFromList(list<TAG_CONTROLCMDDATA*>& List, CRITICAL_SECTION& cs) {    TAG_CONTROLCMDDATA* p = NULL;    EnterCriticalSectionEx(&cs, NULL);    if (!List.empty()) {        p = List.front();        List.pop_front();    }    LeaveCriticalSectionEx(&cs, NULL);    return p;}void RemoveList(list<TAG_CONTROLCMDDATA*>& List, CRITICAL_SECTION& cs) {    TAG_CONTROLCMDDATA* p = NULL;    list<TAG_CONTROLCMDDATA*>::iterator it;    EnterCriticalSectionEx(&cs, _T("RemoveList.0"));    while (!List.empty()) {        p = List.front();        List.pop_front();        if (NULL != p) {            delete p;        }    }    LeaveCriticalSectionEx(&cs, _T("RemoveList.1"));}VOID EnterCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, TCHAR* pTip) {    if (NULL != pTip) {        /// 可以调试下,哪个临界区在多线程中卡住了//        if (NULL != _tcsstr(pTip, _T("11.0")))//            _tprintf(_T("%s\n"), pTip);    }    if (NULL != lpCriticalSection) {        EnterCriticalSection(lpCriticalSection);    }}VOID LeaveCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, TCHAR* pTip) {    if (NULL != lpCriticalSection) {        LeaveCriticalSection(lpCriticalSection);    }    if (NULL != pTip) {        /// only for debug//        if (NULL != _tcsstr(pTip, _T("11.1")))//            _tprintf(_T("%s\n"), pTip);    }}

socket读写封装

// SocketOpt.h: interface for the CSocketOpt class.////////////////////////////////////////////////////////////////////////#if !defined(AFX_SOCKETOPT_H__7FB1AF93_27E4_4722_8F1E_8A6AB9885786__INCLUDED_)#define AFX_SOCKETOPT_H__7FB1AF93_27E4_4722_8F1E_8A6AB9885786__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000#include <windows.h>#include <winsock2.h>int sendEx(SOCKET s, const char FAR *buf, int len, int flags);int recvEx(SOCKET s, char FAR *buf, int len, int flags);#endif // !defined(AFX_SOCKETOPT_H__7FB1AF93_27E4_4722_8F1E_8A6AB9885786__INCLUDED_)
// SocketOpt.cpp: implementation of the CSocketOpt class.////////////////////////////////////////////////////////////////////////#include "stdafx.h"#include "SocketOpt.h"#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE[]=__FILE__;#define new DEBUG_NEW#endif//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////int sendEx(SOCKET s, const char FAR *buf, int len, int flags) {    int iRc = 0;    int iLenOpt = 0;    int iPos = 0;    do {        iRc = send(s, buf + iPos, len - iPos, flags);        if (SOCKET_ERROR == iRc) {            break;        } else {            if (0 == iRc) {                break;            }            iLenOpt += iRc;            iPos -= iRc;        }    } while (len != iLenOpt);    return ((len == iLenOpt) ? len : iRc);}int recvEx(SOCKET s, char FAR *buf, int len, int flags) {    int iRc = 0;    int iLenOpt = 0;    int iPos = 0;    do {        iRc = recv(s, buf + iPos, len - iPos, flags);        if (SOCKET_ERROR == iRc) {            break;        } else {            if (0 == iRc) {                break;            }            iLenOpt += iRc;            iPos -= iRc;        }    } while (len != iLenOpt);    return ((len == iLenOpt) ? len : iRc);}

线程类封装

// MyThread.h: interface for the CMyThread class.////////////////////////////////////////////////////////////////////////#if !defined(AFX_MYTHREAD_H__8B713CD4_E5C4_467D_87C0_B5197689E5C8__INCLUDED_)#define AFX_MYTHREAD_H__8B713CD4_E5C4_467D_87C0_B5197689E5C8__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000#include "stdafx.h"#include <windows.h>#include <process.h>class CMyThread  {public:    typedef unsigned (__stdcall *PFN_THREADPROC)(void*);public:    CMyThread();    virtual ~CMyThread();    void Start(); ///< 线程开始    void Pause(); ///< 线程挂起    void Continue(); ///< 线程恢复运行    void Stop(); ///< 线程停止    BOOL IsRunning(); ///< 是否在运行?    BOOL cbIsCanContinue(); ///< 给线程函数的回调-是否可以继续运行线程    BOOL cbIsNeedQuit(); ///< 给线程函数的回调-是否退出线程    void SetParam(        PFN_THREADPROC pThreadProc,        PFN_THREADPROC pStopProc,        PFN_THREADPROC pDetoryUserDataProc,        void* pUserData);    void* GetUserData();private:    /// 用户传入的参数    PFN_THREADPROC m_pThreadProc; ///< 线程处理函数    PFN_THREADPROC m_pStopProc; ///< 线程停止前的调用者处理, e.g. 解除用户代码的线程阻塞    PFN_THREADPROC m_pDetoryUserDataProc; ///< 请调用者销毁用户数据    void* m_pUserData; ///< 用户数据指针, 我们不动, 创建线程时, 当线程参数    /// 内部数据    HANDLE m_hThread; ///< 线程句柄    UINT m_uThreadId; ///< 线程ID    HANDLE m_hEventContinue; ///< 事件句柄 - 继续    HANDLE m_hEventQuit; ///< 事件句柄 - 退出线程};#endif // !defined(AFX_MYTHREAD_H__8B713CD4_E5C4_467D_87C0_B5197689E5C8__INCLUDED_)
// MyThread.cpp: implementation of the CMyThread class.////////////////////////////////////////////////////////////////////////#include "stdafx.h"#include <windows.h>#include <process.h>#include "MyThread.h"#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE[]=__FILE__;#define new DEBUG_NEW#endif//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////CMyThread::CMyThread(){    m_hThread = NULL;    m_pThreadProc = NULL;    m_pStopProc = NULL;    m_pDetoryUserDataProc = NULL;    m_uThreadId = (UINT)-1;    m_pUserData = NULL;    /// 事件对象用于通知机制时的创建要求, 手工复位, 有信号    m_hEventContinue = CreateEvent(NULL, TRUE, TRUE, NULL);    m_hEventQuit = CreateEvent(NULL, TRUE, TRUE, NULL);}CMyThread::~CMyThread(){    Stop();    if (NULL != m_pDetoryUserDataProc) {        (*m_pDetoryUserDataProc)(m_pUserData);    }}void CMyThread::SetParam(                         PFN_THREADPROC pThreadProc,                         PFN_THREADPROC pStopProc,                         PFN_THREADPROC pDetoryUserDataProc,                         void* pUserData) {    m_pThreadProc = pThreadProc;    m_pStopProc = pStopProc;    m_pDetoryUserDataProc = pDetoryUserDataProc;    m_pUserData = pUserData;}void* CMyThread::GetUserData() {    return m_pUserData;}void CMyThread::Start() {    if (NULL != m_pThreadProc) {        Continue();        if (!IsRunning()) {            m_hThread = (HANDLE)_beginthreadex(                NULL, 0, m_pThreadProc, m_pUserData, 0, &m_uThreadId);        }    }}void CMyThread::Pause() {    ResetEvent(m_hEventContinue);    ResetEvent(m_hEventQuit);}void CMyThread::Continue() {    SetEvent(m_hEventContinue);    ResetEvent(m_hEventQuit);}BOOL CMyThread::cbIsCanContinue() {    WaitForSingleObject(m_hEventContinue, INFINITE);    return TRUE;}BOOL CMyThread::cbIsNeedQuit() {    return (WAIT_OBJECT_0 == WaitForSingleObject(m_hEventQuit, 0));}void CMyThread::Stop() {    if (NULL != m_pStopProc) {        (*m_pStopProc)(m_pUserData);        m_pUserData = NULL;    }    SetEvent(m_hEventQuit);    Continue();    if (NULL != m_hThread) {        WaitForSingleObject(m_hThread, INFINITE);        m_hThread = NULL;    }}BOOL CMyThread::IsRunning() {    DWORD dwRc = 0;    DWORD dw0 = WAIT_ABANDONED; ///< 0x80    DWORD dw1 = WAIT_OBJECT_0; ///< 0x0    DWORD dw2 = WAIT_TIMEOUT; ///< 0x102    if (NULL == m_hThread) {        return FALSE;    }    /// 在线程运行时, 等到 WAIT_TIMEOUT    /// 在线程没有运行时, 返回 WAIT_OBJECT_0    dwRc = WaitForSingleObject(m_hThread, 0);    return (WAIT_OBJECT_0 == dwRc) ? FALSE : TRUE;}

线程类中用户数据上下文类

// SocketRecvData.h: interface for the CSocketRecvData class.////////////////////////////////////////////////////////////////////////#if !defined(AFX_SOCKETRECVDATA_H__B87C87F9_7976_432D_9305_B50010127AE3__INCLUDED_)#define AFX_SOCKETRECVDATA_H__B87C87F9_7976_432D_9305_B50010127AE3__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000#include "MyThread.h"#include "ControlCmd.h"class CSocketRecvData  {public:    CSocketRecvData();    virtual ~CSocketRecvData();    SOCKET& GetSock();    void SetSock(SOCKET s);    CMyThread* GetThreadClass();    void SetThreadClass(CMyThread* p);    TAG_CONTROLCMDDATA_LOGINBYCLIENT& GetLoginInfo();    void UpdateHeartBeatTimestamp();    DWORD GetTickHeartBeatTimestamp();    DWORD GetHeartBeatTimestampCnt();    SYSTEMTIME GetHeartBeatTimestampSystemTime();private:    DWORD tmTickHeartBeatTimestamp; ///< 心跳时间戳    DWORD dwHeartBeatTimestampCnt; ///< 总共收到的时间戳数量    SYSTEMTIME stHeartBeatTimestamp; ///< 心跳时间戳对应的本地时间    SOCKET m_socket;    CMyThread* m_pMyThread;    TAG_CONTROLCMDDATA_LOGINBYCLIENT LoginInfo; ///< 登录信息};#endif // !defined(AFX_SOCKETRECVDATA_H__B87C87F9_7976_432D_9305_B50010127AE3__INCLUDED_)
// SocketRecvData.cpp: implementation of the CSocketRecvData class.////////////////////////////////////////////////////////////////////////#include "stdafx.h"#include "SocketRecvData.h"//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////CSocketRecvData::CSocketRecvData(): dwHeartBeatTimestampCnt(-1){    SetSock(NULL);    SetThreadClass(NULL);    UpdateHeartBeatTimestamp();    ::ZeroMemory(&LoginInfo, sizeof(LoginInfo));    LoginInfo.bLoginOK = FALSE;}CSocketRecvData::~CSocketRecvData(){}void CSocketRecvData::UpdateHeartBeatTimestamp() {    tmTickHeartBeatTimestamp = GetTickCount();    GetLocalTime(&stHeartBeatTimestamp);    dwHeartBeatTimestampCnt++;}DWORD CSocketRecvData::GetHeartBeatTimestampCnt() {    return dwHeartBeatTimestampCnt;}DWORD CSocketRecvData::GetTickHeartBeatTimestamp() {    return tmTickHeartBeatTimestamp;}SYSTEMTIME CSocketRecvData::GetHeartBeatTimestampSystemTime() {    return stHeartBeatTimestamp;}SOCKET& CSocketRecvData::GetSock() {    return m_socket;}void CSocketRecvData::SetSock(SOCKET s) {    m_socket = s;}CMyThread* CSocketRecvData::GetThreadClass() {    return m_pMyThread;}void CSocketRecvData::SetThreadClass(CMyThread* p) {    m_pMyThread = p;}TAG_CONTROLCMDDATA_LOGINBYCLIENT& CSocketRecvData::GetLoginInfo() {    return LoginInfo;}

<2016-0404>fix
发现sendEx和recvEx整错了, 这样都能测试通过,可能是因为数据量小,一次都执行完了

// SocketOpt.cpp: implementation of the CSocketOpt class.////////////////////////////////////////////////////////////////////////#include "stdafx.h"#include "SocketOpt.h"#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE[]=__FILE__;#define new DEBUG_NEW#endif//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////int sendEx(SOCKET s, const char FAR *buf, int len, int flags) {    int iRc = 0;    int iLenOpt = 0;    do {        iRc = send(s, buf + iLenOpt, len - iLenOpt, flags);        if (SOCKET_ERROR == iRc) {            break;        } else {            if (0 == iRc) {                break;            }            iLenOpt += iRc;        }    } while (len != iLenOpt);    return ((len == iLenOpt) ? len : iRc);}int recvEx(SOCKET s, char FAR *buf, int len, int flags) {    int iRc = 0;    int iLenOpt = 0;    do {        iRc = recv(s, buf + iLenOpt, len - iLenOpt, flags);        if (SOCKET_ERROR == iRc) {            break;        } else {            if (0 == iRc) {                break;            }            iLenOpt += iRc;        }    } while (len != iLenOpt);    return ((len == iLenOpt) ? len : iRc);}
0 0