分享
 
 
 

Winsock的事件I/O异步模型(开发网络通信程序入门的继续)

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

前面讨论的开发网络通信的经典入门采用的是WSAAsyncSelect的异步I/O模型,本文将讨论WSAEventSelect异步I/O模型。

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_)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有