Winsock异步模型之二(WSAEventSelect)

目录:
一、WinSock I/O模型分析
1.WSAAsuncSelect模型
2.WSAEventSelect模型
3.重叠(Overlapped)I/O模型
二、聊天软件的设计与分析
1.程序功能
2.程序设计
3.实现是的关键点
三、体会
四、源代码

一、WinSock I/O模型分析

Winsock提供了五种套接字I/O模型来解决这些问题.他们分别是select(选择),WSAAsyncSelect(异步选择),WSAEventSelect (事件选择), overlapped(重叠) , completion port(完成端口) .

1.WSAAsyncSelect 模型

WSAAsyncSelect模型是Windows下最简单易用的一种Socket I/O模型。使用这种模型时,Windows会把网络事件以消息的形势通知应用程序。使用此模型,应用程序可在一个套接字上进行以windows消息为基 础的网络事件通知,此模型提供了读写数据能力的异步通知,但不提供异步数据传送。异步数据传送有”重叠及完成端口模型提供”。 WSAAsyncSelect提供了一个最适合 Windows 自己运作模型的工作方式。它可以把 socket 的消息映射到线程的消息循环中。通过 WSAAsyncSelect 设置,线程消息循环将在指定的事件发生后,得到相应的消息。WSAGETSELECTEVENT(lParam) 可以用来得到网络事件本身,而 wParam 则被用来传递 socket 的 handle 。然后,就可以主动调用 socket 函数来处理这些事件了。这比 select 的模型更适合 Windows 应用程序。而 windows 的应用程序的主体永远只需要一个简单的循环来处理和分发消息就够了。

优点:可在系统开销不大的情况下同时处理许多连接。

缺点:即使用不需要窗口(如服务器,控制台)它也不得不额外使用一个窗口。同时如果处理成千上万套接字的所有事件,性能可想而知。

以下为此模型的相关信息:

1).定义消息

#define WM_SOCKET WM_USER+101

2).添加消息

BEGIN_MESSAGE_MAP(CMyGameDlg, CDialog)
ON_MESSAGE(WM_SOCKET,OnSocket)
END_MESSAGE_MAP()

3).客户端和服务器的绑定

服务器:

bind(m_sockListen,(PSOCKADDR)&addr,sizeof(addr) );
WSAAsyncSelect(m_sockListen,this->m_hWnd,WM_SOCKET,FD_ACCEPT|FD_CLOSE);
listen(m_sockListen,5);

客户端:

WSAAsyncSelect(m_sockConnect,this->m_hWnd,WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE);

connect( m_sockConnect,(SOCKADDR*)&addr,sizeof(addr) );

4).添加处理函数

long CMyGameDlg::OnSocket(WPARAM wParam,LPARAM lParam)
{
if(WSAGETSELECTERROR(lParam))
{
closesocket( (SOCKET)wParam );
MessageBox(”WSAGETSELECTERROR”);
return 0;
}

switch( WSAGETSELECTEVENT(lParam) )
{
case FD_ACCEPT:
{
m_sockConnect = accept( (SOCKET)wParam,NULL,NULL);
closesocket((SOCKET)wParam);
WSAAsyncSelect(m_sockConnect,this->m_hWnd,WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE);
break;
}
case FD_READ:
{
char cstrRecv[1024];
recv(m_sockConnect,cstrRecv,1024,0);
m_listboxMessage.InsertString(-1,cstrRecv);
break;
}
case FD_WRITE:
{
m_editSendMsg.EnableWindow();
break;
}
case FD_CLOSE:
{
break;
}
default:
break;
}
return 0;
}

WSAAsyncSelect模型最突出的特点是与Windows的消息驱动机制融在了一起,这使得开发带GUI界面的网络程序变得简单。但是如果连接增加,单个Windows函数处理上千客户请求时,服务器性能势必会受到影响。

2.WSAEventSelectI/O 模型

WSAEventSelectI/O模型是Winsock提供了另一个有用的异步I/O模型,它也允许应用程序在一个 或多个套接字上,并能够接收以事件为基础的网络事件通知,一次对一个或多个套接字上进行的通信加以管理。WSAEventSelect函数对条算使用的每 一个套接字创建一个事件对象(即网络事件和一个事件对象句柄关联起来),返回一个创建好的事件对象句柄,然后通过 WSAWaitForMultileEvents函数等待一个或多个网络事件触发事件的对象句柄的工作状态了。若 WSAWaitForMultileEvents收到一个事件对像的网络事件通知,便会返回一个值,指出造成函数返回的事件对像,这样便可以引用事件数组 中已传信的事件,并检索与那个事件对应的套接字,判断到底那个套接字上,发生了什么网络事件类型。网络事件一旦触发了与一个套接字关联在一起的事件对像, 事件的工作状态便会从”未传信”转变成”已传信”,在完成了一个I/O请求的处理这后,事件的工作状态由已传信改为未传信(调用 WSAResetEvent函数),最后再调用WSACloseEvent函数,释放由事件句柄使用的系统资源。WSAEventSelect模型与 WSAAsyncSelect模型类似,差别在于前者的网络事件是由事件句柄完成,后者的网络事件是由窗口过程完成。

优点:在于概念简单,不需要窗口环境。

缺点:在于每次只能等待64个事件,这就是说在处理多天64个的SOCKET时必须组织一个线程池。如需处理大量的SOCKET连接,此模型的伸缩性就不如重叠模型。

以下为此模型的相关信息:

1).为每个打算使用的套接字创建一个事件对象,创建方法:WSAEVENT WSACreateEvent(void)返回值是人工重设的事件对象句柄。

2).创建了事件对象后必须将它与某个套接字关联在一起,同时注册感兴趣的事件类型。

int WSAEventSelect (

SOCKET s , 相关套接字

WSAEVENT hEventObject , 事件句柄

long lNetworkEvents 网络事件(参见MSDN)

);

事件分为”人工重设”和”自动重设”两种,因为WSACreateEvent创建的为人工重设,在完成一个I/O请求后 事件就变为已经传信状态,所以要自己将已传信的工作状态变为未传信状态,方法为使用WSAResetEvent重置事件状态。应用程序完成了对某个事件对 象的处理后应调用WSACloseEvent来释放由事件句柄使用的系统资源。

3).SOCKET同一个事件对象关联在一起后,程序便可开始I/O处理,这就需要应用程序等待网络事件来触发事件对象句柄的工作状态。使用WSAWaitForMultipleEvents函数来等待网络事件的触发。

DWORD WSAWaitForMultipleEvents(

DWORD cEvents , 事件的数量

const WSAEVENT FAR *lphEvents , 事件数组

BOOL fWaitAll , 等待数组的状态,TRUE时只有在lphEvents 数组中所有事件对象都已传信才返回,反之

DWORD dwTimeOUT , 等待网络事件发生的时间

BOOL fAlertable 此参数设为FALSE,主要用在重叠I/O的完成例程中,在此无具体意义

);

若收到一个事件对象的网络事件通知后,便会返回一个值指出造成函数返回的事件对象,这样应用程序便可引用事件数组中已传 信的事件,并检索与事件对应的SOCKET,方法是用WSAWaitForMultipleEvents的返回值减去相应的预定义值 WSA_WAIT_EVENT_0,得到具体的引用值,如下所示

Index = WSAWaitForMultipleEvents(…)

myEvent = EventArray[Index - WSA_WAIT_EVENT_0];

4).知道了造成网络事件的SOCKET后,并可调用WSAENumNetWorkEvents函数,查看发生的什么网络事件,函数定义如下.

int WSAEnumNetworkEvents (

SOCKET s , 造成网络事件的SOCKET

WSAEVENT hEventObject , 打算重设的事件对象

LPWSANETWORKEVENTS lpNetworkEvents 指向 WSANETWORKEVENTS 结构的指针

);

5).完成了对WSANETWORKEVENTS结构中事件的处理后,应用程序在所有可用的SOCKET上继续等待更多的网络事件。

主要代码如下所示:(创建到bind省略)

SOCKET SocketArray[WSA_MAXIMUM_WAIT_EVENTS]; //SOCKET数组

WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS], NewEvent;//事件数组

WSANETWORKEVENTS NetworkEvents; //网络事件

DWORD EventTotal = 0, Index = 0, i=0;

…相应的创建和bind

NewEvent = WSACreateEvent();

WSAEventSelect(Listen, NewEvent, FD_ACCEPT | FD_CLOSE);

listen(Listen, 5);

SocketArray[EventTotal] = Listen;

EventArray[EventTotal] = NewEvent;

EventTotal ++;

While (TRUE)

{

//等待所有SOCKET上的网络事件

Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE,

WSA_INFINITE, FALSE);

Index = Index - WSA_WAIT_EVENT_0; //发生网络事件的索引

//遍历所有事件,查看是否多天一个的事件被传信

for(int I = Index; i< EventTotal; i++)

{

Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE,

1000, FALSE);

if ((Index == WSA_WAIT_FAILED) || (Index == WSA_WAIT_TIMEOUT))

continue;

else

{

Index = I;

WSAEnumNetworkEvents(SocketArray[Index],

EventArray[Index],

&NetworkEvents);

//检查消息FD_ACCEPT

If(NetworkEvents.lNetworkEvents & FD_ACCEPT)

{

If(NetworkEvents.iErrorCode[FD_ACCEPT_BIT] != 0)

{

Printf(”FD_ACCEPT failed with error %d/n,

NetworkEvents.iErrorCode[FD_ACCEPT_BIT]);

}

//接受新连接并加入SOCKET列表中

Accept = accept(SocketArray[Index], NULL, NULL);

If (EventTotal > WSA_WAIT_EVENTS)

{

Printf(”Too manyconnects”);

Closesocket(Accept);

Break;

}

NewEvent = WSACreateEvent();

WSAEventSelect(Accept, NewEvent, FD_READ | FD_CLOSE);

SocketArray[EventTotal] = Accept;

EventArray[EventTotal] = NewEvent;

EventTotal ++;

Printf(”Socket %d Connected /n”, Accept);

}//end FD_ACCEPT

//处理FD_READ

If(NetworkEvents.lNetworkEvents & FD_READ)

{

If(NetworkEvents.iErrorCode[FD_ READ _BIT] != 0)

{

Printf(”FD_ READ failed with error %d/n,

NetworkEvents.iErrorCode[FD_ READ_BIT]);

}

Recv(SocketArray[Index - WSA_WAIT_EVENT_0], buffer,

sizeof(buffer), 0);

…相应其它处理

} //end FD_READ if

//FD_CLOSE消息

If(NetworkEvents.lNetworkEvents & FD_CLOSE)

{

If(NetworkEvents.iErrorCode[FD_ CLOSE _BIT] != 0)

{

Printf(”FD_CLOSE failed with error %d/n,

NetworkEvents.iErrorCode[FD_ CLOSE_BIT]);

}

Closesocket(SocketArray[Index]);

//从Socket和Event数组删除SOCKET及相关EVENT并递减EventTotal

compressArrays(EventArray, SocketArray, &EventTotal);

} //end FD_CLOSE if

}//end if else

} //end for

} //end while

WSAEventSelect模式简单易用,也不需要窗口环境。该模型惟一的缺点是有最多等待64个事件对象的限制,当套接字连接数量增加时,就必须创建多个线程来处理I/O,也就是使用所谓的线程池。

3. 重叠(Overlapped)I/O模型

重叠I/O也是一种异步I/O,同样也支持Win32的其它对象,当然在Winsock中可以发挥很大的作用。使用Overlapped开发支持一定数量的Socket 的 应用,效率是相当很高的。重叠就是一种异步处理的说法,一边向socket 投递操作,而在另一边等待结果的完成,两边互不相干。重叠I/O模型基本设计思想是允许应用程序提交的I/O请求完成之后,与之关联的重叠数据结构中的事 件对象受信,应用程序便可使用WSAGetOverlappedResult函数获取重叠操作结果。

重叠I/O模型可使应用程序得到更好的性能,其原理是让应用程序使用一个重叠数据结构一次投递一个或者多个异步I/O请 求。使用该模型时应先用WSA_FLAG_OVERLAPPED这个标志创建套接字,成功创造一个套接字,同时将一个本地接口绑定到一起,便可进行重叠操 作。管理重叠请求的方法有两种:”等待事件对像通知”和”完成例程”。

优点:可以运行在支持Winsock2的所有Windows平台 ,而不像完成端口只是支持NT系统。比起阻塞、select、WSAAsyncSelect以及WSAEventSelect等模型,重叠I/O (Overlapped I/O)模型使应用程序能达到更佳的系统性能。

以下为此模型的相关信息:

重叠模型的使用步骤:

创建一个带Overlapped标志的Socket句柄(其实也可以是文件句柄);

创建带Overlapped标志的Socket句柄:

SOCKET sListen;
if((sListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
OutErr(”WSASocket error!”);
return;
}

准备好一个Overlapped对象,还有hEvent事件:

// 准备好一个给 listenSocket 使用的 Overlapped
ZeroMemory(&ListenOverlapped, sizeof(OVERLAPPED));
ListenOverlapped.hEvent = WSACreateEvent();

对这个句柄投递操作,这是一个侦听句柄,当然就是给他投递AcceptEx操作:

AcceptEx(sListen, sClient, (PVOID) szAcceptBuffer, 0,
sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwRecvBytes,
&ListenOverlapped);

在一个循环或者线程中等待事件的完成,并取得结果做处理:

while(TRUE)
{
// 等待侦听事件的发生
nIdx = WSAWaitForMultipleEvents(1, &ListenOverlapped.hEvent, FALSE, INFINITE, FALSE);

// 取得这次Overlapped的结果
WSAGetOverlappedResult(sListen, &ListenOverlapped, &dwTransferred, FALSE, &dwFlags);

cout << sClient << ” 连接上来了!” << endl;
cout << “连接总数为:” << ++g_nClientCnt << endl;
// 重置一下事件
WSAResetEvent(ListenOverlapped.hEvent);

// 再重新向ListenSocket投递一个AcceptEx请求
sClient = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
ZeroMemory(szAcceptBuffer, 2*(sizeof(SOCKADDR_IN) + 16) );
AcceptEx(sListen, sClient, (PVOID) szAcceptBuffer, dwRecvData,
sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwRecvBytes,
&ListenOverlapped);
}

二、聊天软件的设计与分析

1. 程序功能

此聊天程序将实现以下功能:

1)以主机为用户名,简单的输入和接受窗体。

2)局域网内用户电脑安装运行程序都可接收任何人的信息进行交流。

3)采用VC++6.0MFC应该程序的方式编程,用对话框的形式输入和显示;

4)为防止多个用户线程同时连接时出现阻塞,服务器将采用Select函数监视多个连接;

5)每个人的消息框都可以显示所有人的聊天信息。

2. 程序设计

1)主界面分两部分。对话框和消息框。对话框有IP控件添写自己主机IP地址,输入发送消息。消息框接受所有用户所发的消息,显示如”127.0.0.1说:。。。。”。

2)有发送键,输完信息,按发送或回车,即可发送信息。

3)网络编程所以运用套接字。先加载套接字库,进行版本协商。我采用AfxSocketInit加载1.1版本套接字库。最后调用WSALeanup函数终止套接字库调用。

4)实现把消息发送到每一个用户的功能:用一个循环,对每一个连接套接字(包括发送的那个用户)都传送用户IP和数据。

3. 步骤

1)用函数AfxSocketInit加载套接字库。

2)建立头文件。在StdAfx.h代码下写入#include <Afxsock.h>

3)定义成员函数、成员变量。

4)创建数据包套接字。

5)定义地址结构体。

6)创建线程。接受和传输数据。

7)编写发送与接受数据代码。

8)编写发送按钮代码

4 、实现中的关键点

1)掌握多线程模式。

2)加载套接字库。

3)定义成员函数、成员变量。

4)创建线程。接受与发送程序。

5)实现

三、体会

通过此次课程设计,我更好的学系了几种Winsock I/O模型学习并应用。还学习了多线程的相关知识,所以才编写出聊天程序。因为不数很熟练,所以在创建套接字和函数时经常出错。有时也会因为拼写错误。对 VC++地应用也是同样。不过通过一段时间的调试,基本可以找到错误的原因并改正。不过还有不少等待研究。通过设计我对网络通信有了些了解,并想继续学 习。

四、源代码

因为源代码比较多,下面是主程序代码:

// CpliaotianDlg.cpp : implementation file

//

#include “stdafx.h”

#include “Cpliaotian.h”

#include “CpliaotianDlg.h”

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/

// CAboutDlg dialog used for App About

class 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

// Implementation

protected:

//{{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_MAP

END_MESSAGE_MAP()

/

// CCpliaotianDlg dialog

CCpliaotianDlg::CCpliaotianDlg(CWnd* pParent /*=NULL*/)

: CDialog(CCpliaotianDlg::IDD, pParent)

{

//{{AFX_DATA_INIT(CCpliaotianDlg)

// NOTE: the ClassWizard will add member initialization here

//}}AFX_DATA_INIT

// Note that LoadIcon does not require a subsequent DestroyIcon in Win32

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

}

void CCpliaotianDlg::DoDataExchange(CDataExchange* pDX)

{

CDialog::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CCpliaotianDlg)

// NOTE: the ClassWizard will add DDX and DDV calls here

//}}AFX_DATA_MAP

}

BEGIN_MESSAGE_MAP(CCpliaotianDlg, CDialog)

//{{AFX_MSG_MAP(CCpliaotianDlg)

ON_WM_SYSCOMMAND()

ON_WM_PAINT()

ON_WM_QUERYDRAGICON()

ON_BN_CLICKED(IDC_BTN_SEND, OnBtnSend)

//}}AFX_MSG_MAP

ON_MESSAGE(WM_RECVDATA,OnRecvData)

END_MESSAGE_MAP()

/

// CCpliaotianDlg message handlers

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

InitSocket();

RECVPARAM *pRecvParam=new RECVPARAM;

pRecvParam->sock=m_socket;

pRecvParam->hwnd=m_hWnd;

HANDLE hThread=CreateThread(NULL,0,RecvProc,(LPVOID)pRecvParam,0,NULL);

CloseHandle(hThread);

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

}

void CCpliaotianDlg::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 CCpliaotianDlg::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 CCpliaotianDlg::OnQueryDragIcon()

{

return (HCURSOR) m_hIcon;

}

BOOL CCpliaotianDlg::InitSocket()

{

m_socket=socket(AF_INET,SOCK_DGRAM,0);

if(INVALID_SOCKET==m_socket)

{

MessageBox(”套接字创建失败!”);

return FALSE;

}

SOCKADDR_IN addrSock;

addrSock.sin_family=AF_INET;

addrSock.sin_port=htons(6000);

addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

int retval;

retval=bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR));

if(SOCKET_ERROR==retval)

{

closesocket(m_socket);

MessageBox(” 绑定失败!”);

return FALSE;

}

return TRUE;

}

DWORD WINAPI CCpliaotianDlg::RecvProc(LPVOID IpParameter)

{

SOCKET sock=((RECVPARAM*)IpParameter)->sock;

HWND hwnd=((RECVPARAM*)IpParameter)->hwnd;

SOCKADDR_IN addrFrom;

int len=sizeof(SOCKADDR);

char recvBuf[200];

char tempBuf[300];

int retval;

while(TRUE)

{

retval=recvfrom(sock,recvBuf,200,0,(SOCKADDR*)&addrFrom,&len);

if(SOCKET_ERROR==retval)

break;

sprintf(tempBuf,”%s说:%S”,inet_ntoa(addrFrom.sin_addr),recvBuf);

::PostMessage(hwnd,WM_RECVDATA,0,(LPARAM)tempBuf);

}

return 0;

}

void CCpliaotianDlg::OnRecvData(WPARAM wParam,LPARAM lParam)

{

CString str=(char*)lParam;

CString strTemp;

GetDlgItemText(IDC_EDIT_RECV,strTemp);

str+=”/r/n”;

str+=strTemp;

SetDlgItemText(IDC_EDIT_RECV,str);

}

void CCpliaotianDlg::OnBtnSend()

{

// TODO: Add your control notification handler code here

DWORD dwIP;

((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);

SOCKADDR_IN addrTo;

addrTo.sin_family=AF_INET;

addrTo.sin_port=htons(6000);

addrTo.sin_addr.S_un.S_addr=htonl(dwIP);

CString strSend;

GetDlgItemText(IDC_EDIT_SEND,strSend);

sendto(m_socket,strSend,strSend.GetLength()+1,0,

(SOCKADDR*)&addrTo,sizeof(SOCKADDR));

SetDlgItemText(IDC_EDIT_SEND,”");

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值