Winsock的事件I/O异步模型——WSAEventSelect

   WSAEventSelect模型有点类似WSAAsyncSelect模型,不同的是他不是用消息映射的方式来响应网络事件,而是用等待多重事件的方式来响应网络事件。下面是用WSAEventSelect模型和多线程机制做的一个简单的服务器程序的.cpp和.h文件,应用程序基于MFC的标准对话框。实现接受多个客户端的连接请求,并记录下所有客户端的相关信息,显示在列表框中。

// serverDlg.cpp : implementation file
//

#include "stdafx.h"
#include "server.h"
#include "serverDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

SOCKET Accept; file://用于新的一个连接通信的套接字
WSAEVENT NewEvent; file://对应于新的套接字的新事件
SOCKET Socket[WSA_MAXIMUM_WAIT_EVENTS]; file://存放所有生成的套接字
WSAEVENT Event[WSA_MAXIMUM_WAIT_EVENTS]; file://存放所有生成的事件对象
int EventTotal; file://创建的事件总数
int Index;      file://等待多重事件函数的返回值
WSANETWORKEVENTS NetworkEvents; file://用于接收套接字上发生的网络事件类型以及可能出现的错
误代码

/
// CAboutDlg dialog used for App About

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

// Dialog Data
file://{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
file://}}AFX_DATA

// ClassWizard generated virtual function overrides
file://{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
file://}}AFX_VIRTUAL

// Implementation
protected:
file://{{AFX_MSG(CAboutDlg)
file://}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
file://{{AFX_DATA_INIT(CAboutDlg)
file://}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
file://{{AFX_DATA_MAP(CAboutDlg)
file://}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
file://{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
file://}}AFX_MSG_MAP
END_MESSAGE_MAP()

/
// CServerDlg dialog

CServerDlg::CServerDlg(CWnd* pParent /*=NULL*/)
: CDialog(CServerDlg::IDD, pParent)
{
file://{{AFX_DATA_INIT(CServerDlg)
// NOTE: the ClassWizard will add member initialization here
file://}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_Connectnum = 0;
m_NetworkID = 0;
EventTotal = 0;
    for(int i = 0; i < MAX_CLIENT_NUM; i++)
{
ZeroMemory(&m_ClientInfo[i], sizeof(client_info));
}

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CServerDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
file://{{AFX_DATA_MAP(CServerDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
file://}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CServerDlg, CDialog)
file://{{AFX_MSG_MAP(CServerDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_TIMER()
file://}}AFX_MSG_MAP
END_MESSAGE_MAP()

/
// CServerDlg message handlers

BOOL CServerDlg::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

WSADATA wsaData;
int ret;

ret = WSAStartup(MAKEWORD(2,2), &wsaData);
if(ret != 0)
{
MessageBox("初始化套接字失败!");
return FALSE;
}

file://创建一个套接字
m_ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(m_ListenSocket == INVALID_SOCKET)
{
MessageBox("创建套接字失败!");
closesocket(m_ListenSocket);
WSACleanup();
return FALSE;
}

file://绑定到指定的端口上
sockaddr_in localaddr;
localaddr.sin_family = AF_INET;
localaddr.sin_port = htons(1688);
localaddr.sin_addr.s_addr = 0;

if(bind(m_ListenSocket, (const struct sockaddr*)&localaddr, sizeof(sockaddr))
                                                                 == SOCKET_ERROR)
{
MessageBox("绑定地址失败!");
closesocket(m_ListenSocket);
WSACleanup();
return FALSE;
}

NewEvent = WSACreateEvent(); file://创建一个新的事件对象

file://将创建的事件对象与前面创建的套接字关联在一起,并注册网络事件类型
        if(WSAEventSelect(m_ListenSocket, NewEvent, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR)
{
MessageBox("注册网络事件失败!");
closesocket(m_ListenSocket);
WSACleanup();
return FALSE;
}

file://让创建的套接字处于监听状态
listen(m_ListenSocket, 5);

Event[EventTotal] = NewEvent;
Socket[EventTotal] = m_ListenSocket;
EventTotal++;

        file://设置List控件的图象列表
HICON hIcon;

m_imagelist.Create(16, 16, 0, 4, 4); // 32, 32 for large icons
hIcon = AfxGetApp()->LoadIcon(IDI_CLIENT_INFO);

m_imagelist.SetBkColor (RGB(248,232,224));
m_imagelist.Add(hIcon);

pList = (CListCtrl*)GetDlgItem(IDC_CLIENT_INFO);
pList->SetImageList(&m_imagelist, LVSIL_SMALL);
pList->SetBkColor(RGB(248,232,224));
pList->SetTextBkColor(RGB(248,232,224));

pList->InsertColumn(0,"   客户名",LVCFMT_CENTER,90, 0);
pList->InsertColumn(1,"网络ID",LVCFMT_CENTER,50,1);
pList->InsertColumn(2,"IP地址",LVCFMT_CENTER,100,2);
pList->InsertColumn (3,"登录时间",LVCFMT_CENTER,120,3);
pList->InsertColumn (4,"在线时间",LVCFMT_CENTER,100,4);

SetTimer(1, 1000, NULL);

file://启动核心处理线程
AfxBeginThread(KernelWorkThread,this,THREAD_PRIORITY_NORMAL);

return TRUE; // return TRUE unless you set the focus to a control
}

void CServerDlg::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 CServerDlg::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 CServerDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}

file://核心处理线程, 响应并处理各种网络事件
UINT KernelWorkThread(LPVOID pParam)
{
int len = sizeof(sockaddr);

CServerDlg* dlg;
dlg = (CServerDlg*)pParam;

while(1)
{
         Index = WSAWaitForMultipleEvents(EventTotal, Event, FALSE, WSA_INFINITE, FALSE);

WSAEnumNetworkEvents(Socket[Index - WSA_WAIT_EVENT_0],
                     Event[Index - WSA_WAIT_EVENT_0],
                                     &NetworkEvents);

if(NetworkEvents.lNetworkEvents & FD_ACCEPT)
file://连接事件
{
   if(NetworkEvents.iErrorCode[FD_ACCEPT_BIT] != 0)
   {
    dlg->MessageBox("接受连接事件失败!");
    break;
   }

   Accept = accept(Socket[Index - WSA_WAIT_EVENT_0],
                                 (struct sockaddr*)&(dlg->clientaddr), &len);
   if(Accept == INVALID_SOCKET)
   {
    dlg->MessageBox("接受连接失败!");
    break;
   }
  
   if(EventTotal > WSA_MAXIMUM_WAIT_EVENTS)
   {
    dlg->MessageBox("连接个数溢出,拒绝接受!");
    break;
   }

   NewEvent = WSACreateEvent();

   if(WSAEventSelect(Accept, NewEvent, FD_READ | FD_WRITE | FD_CLOSE)
                                                                   == SOCKET_ERROR)
   {
    dlg->MessageBox("注册网络事件失败!");
    closesocket(Accept);
    break;
   }

   Event[EventTotal] = NewEvent;
   Socket[EventTotal] = Accept;
   EventTotal ++;
}

if(NetworkEvents.lNetworkEvents & FD_READ)
file://读取数据事件
{
   if(NetworkEvents.iErrorCode[FD_READ_BIT] != 0)
   {
    dlg->MessageBox("读事件失败!");
    break;
   }

   if(dlg->OnReceive(Socket[Index - WSA_WAIT_EVENT_0]) == FALSE)
   {
    dlg->MessageBox("读取数据失败!");
    break;
   }
}

if(NetworkEvents.lNetworkEvents & FD_CLOSE)
file://关闭套接字事件
{
   if(NetworkEvents.iErrorCode[FD_CLOSE_BIT] != 0)
   {
    dlg->MessageBox("关闭事件失败!");
    break;
   }

   if(dlg->OnClose(Socket[Index - WSA_WAIT_EVENT_0]) == FALSE)
   {
    dlg->MessageBox("关闭套接字失败!");
    break;
   }
}
}

return 0;
}

BOOL CServerDlg::OnClose(SOCKET pSocket)
{
int i, exitnum;

for(i = 0; i < m_Connectnum; i++)
{
if(m_ClientInfo[i].Client_Socket == pSocket)
{
   exitnum = i;
}
}
for(i = exitnum; i < m_Connectnum; i++)
{
memcpy(&m_ClientInfo[i], &m_ClientInfo[i+1], sizeof(client_info));
}

m_Connectnum --;

file://向所有客户端发送在线客户信息的报文
cmd_client_info ClientInfo;
ClientInfo.cmd_type = CMD_CLIENT_INFO;
ClientInfo.client_num = m_Connectnum;

for(i=0; i<=m_Connectnum; i++)
{
ClientInfo.Networks_ID[i] = m_ClientInfo[i].Network_ID;
strcpy(ClientInfo.users_name[i], m_ClientInfo[i].User_Name);
strcpy(ClientInfo.clients_ipaddr[i], inet_ntoa(m_ClientInfo[i].Client_Addr.sin_addr));
}
for(i=0; i<=m_Connectnum; i++)
{
send(m_ClientInfo[i].Client_Socket, (char*)&ClientInfo, sizeof(cmd_client_info), NULL);
}
closesocket(pSocket);

        pList->DeleteItem(exitnum);

return TRUE;
}

BOOL CServerDlg::OnReceive(SOCKET pSocket)
{
    static char rcvbuf[65535];   file://接收缓冲区
    int ret;
int offset=0;
find_type* pFindType;
int i = 0;
CTime m_current_time=CTime::GetCurrentTime ();
CString strTime = m_current_time.Format("%c");
CString networkid; file://列表框的网络ID项

    ret = recv(pSocket, rcvbuf, 65535, 0);
if(ret == OPERATION_ERROR)
return FALSE;

while(offset < ret)
{
pFindType = (find_type*)(rcvbuf+offset);
switch(pFindType->cmd_type)
{
case CMD_HELLO:
   cmd_hello Hello;
   memcpy(&Hello, rcvbuf+offset, sizeof(cmd_hello));
   offset+=sizeof(cmd_hello);

   cmd_hello_resp HelloResp;
   m_NetworkID ++;
   HelloResp.cmd_type = CMD_HELLO_RESP;
   HelloResp.Network_ID = m_NetworkID;
   strcpy(HelloResp.user_name, Hello.user_name);

   memcpy((struct sockaddr*)&(m_ClientInfo[m_Connectnum].Client_Addr),
    (const struct sockaddr*)&clientaddr, sizeof(sockaddr));
   m_ClientInfo[m_Connectnum].Client_Socket = Accept;
   strcpy(m_ClientInfo[m_Connectnum].User_Name, HelloResp.user_name);
   m_ClientInfo[m_Connectnum].Network_ID = m_NetworkID;
   m_ClientInfo[m_Connectnum].Login_Time = m_current_time;
   send(pSocket, (char*)&HelloResp, sizeof(cmd_hello_resp), NULL);

   file://向登录的客户端发送回应报文
   Sleep(200);

   cmd_client_info ClientInfo;
   ClientInfo.cmd_type = CMD_CLIENT_INFO;
   ClientInfo.client_num = m_Connectnum +1;

   for(i=0; i<=m_Connectnum; i++)
   {
    ClientInfo.Networks_ID[i] = m_ClientInfo[i].Network_ID;
    strcpy(ClientInfo.users_name[i], m_ClientInfo[i].User_Name);
                                strcpy(ClientInfo.clients_ipaddr[i],
                                       inet_ntoa(m_ClientInfo[i].Client_Addr.sin_addr));
   }

   file://向所有在线客户端发送在线客户信息报文
   for(i=0; i<=m_Connectnum; i++)
   {
    send(m_ClientInfo[i].Client_Socket, (char*)&ClientInfo,
                                     sizeof(cmd_client_info), NULL);
   }

   file://刷新客户端信息列表
   networkid.Format("%d", m_NetworkID);

   LVITEM lvinsert;
   lvinsert.mask=LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM;
   lvinsert.iItem=m_Connectnum;
   lvinsert.iSubItem=0;
   lvinsert.cchTextMax=20;
   lvinsert.pszText=HelloResp.user_name;
   lvinsert.iImage = 0;
   pList->InsertItem (&lvinsert);
   pList->SetItemText (m_Connectnum,1,networkid);
   pList->SetItemText(m_Connectnum,2,
                               inet_ntoa(m_ClientInfo[m_Connectnum].Client_Addr.sin_addr));
   pList->SetItemText (m_Connectnum,3,strTime);

   m_Connectnum ++;

   break;
case CMD_ASK:
   cmd_ask Ask;
   cmd_ask_resp AskResp;
   memcpy(&Ask,rcvbuf+offset,sizeof(cmd_ask));
   offset+=sizeof(cmd_ask);
   AskResp.cmd_type = CMD_ASK_RESP;
   AskResp.Network_ID = Ask.Network_ID;
   for(i=0; i<m_Connectnum; i++)
   {
    if(m_ClientInfo[i].Network_ID == Ask.Network_ID)
    {
     strcpy(AskResp.pData1,m_ClientInfo[i].User_Name);
     strcat(AskResp.pData1, ":");
    }
   }
   strcpy(AskResp.pData2, Ask.pData);
   for(i=0; i<m_Connectnum; i++)
   {
    send(m_ClientInfo[i].Client_Socket, (char*)&AskResp, sizeof(AskResp), 0);
   }

   break;
case CMD_GOODBYE:
   closesocket(pSocket);
   break;
default:
   break;
}
}

return TRUE;
}
BOOL CServerDlg::OnSend(SOCKET pSocket)
{
return TRUE;
}

void CServerDlg::OnOK()
{
closesocket(m_ListenSocket);
WSACleanup();
CDialog::OnOK();
}

void CServerDlg::OnTimer(UINT nIDEvent)
{
CTime m_current_time = CTime::GetCurrentTime();
CTimeSpan logintimes;
CString login_times;
CString networkid; file://列表框的网络ID项

for(int i=0; i<m_Connectnum; i++)
{
logintimes = m_current_time - m_ClientInfo[i].Login_Time;
login_times.Format("%d小时%d分%d秒", logintimes.GetHours(),
                                    logintimes.GetMinutes(),
            logintimes.GetSeconds());

pList->SetItemText (i,4,login_times);
}

CDialog::OnTimer(nIDEvent);
}


// serverDlg.h : header file
//

#if !defined(AFX_SERVERDLG_H__B0AA0367_C1F4_11D4_AB1C_0080C8D6FEA5__INCLUDED_)
#define AFX_SERVERDLG_H__B0AA0367_C1F4_11D4_AB1C_0080C8D6FEA5__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "global.h"

/
// CServerDlg dialog

class CServerDlg : public CDialog
{
file://全局函数
    friend UINT KernelWorkThread(LPVOID pParam);
// Construction
public:
CListCtrl* pList; file://客户端在线信息列表框对象
CImageList m_imagelist;

SOCKET m_ListenSocket; file://用于监听端口的套接字
client_info m_ClientInfo[MAX_CLIENT_NUM]; file://保存在线客户端信息的结构体数组
sockaddr_in clientaddr; file://保存发起连接的客户端地址
int m_Connectnum; file://在线客户端个数
int m_NetworkID; file://返回给客户端的网络ID号

BOOL OnSend(SOCKET pSocket);    file://发送数据网络事件的响应函数
BOOL OnReceive(SOCKET pSocket); file://接收数据网络事件的响应函数
BOOL OnClose(SOCKET pSocket);   file://关闭套接字网络事件的响应函数

CServerDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
file://{{AFX_DATA(CServerDlg)
enum { IDD = IDD_SERVER_DIALOG };
// NOTE: the ClassWizard will add data members here
file://}}AFX_DATA

// ClassWizard generated virtual function overrides
file://{{AFX_VIRTUAL(CServerDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
file://}}AFX_VIRTUAL

// Implementation
protected:
HICON m_hIcon;

// Generated message map functions
file://{{AFX_MSG(CServerDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
virtual void OnOK();
afx_msg void OnTimer(UINT nIDEvent);
file://}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

file://{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_SERVERDLG_H__B0AA0367_C1F4_11D4_AB1C_0080C8D6FEA5__INCLUDED_)

FROM:http://blog.donews.com/mono/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值