WSAAnsycSelect.docx

上传人:聆听****声音 文档编号:2030893 上传时间:2023-05-02 格式:DOCX 页数:25 大小:23.84KB
下载 相关 举报
WSAAnsycSelect.docx_第1页
第1页 / 共25页
WSAAnsycSelect.docx_第2页
第2页 / 共25页
WSAAnsycSelect.docx_第3页
第3页 / 共25页
WSAAnsycSelect.docx_第4页
第4页 / 共25页
WSAAnsycSelect.docx_第5页
第5页 / 共25页
WSAAnsycSelect.docx_第6页
第6页 / 共25页
WSAAnsycSelect.docx_第7页
第7页 / 共25页
WSAAnsycSelect.docx_第8页
第8页 / 共25页
WSAAnsycSelect.docx_第9页
第9页 / 共25页
WSAAnsycSelect.docx_第10页
第10页 / 共25页
WSAAnsycSelect.docx_第11页
第11页 / 共25页
WSAAnsycSelect.docx_第12页
第12页 / 共25页
WSAAnsycSelect.docx_第13页
第13页 / 共25页
WSAAnsycSelect.docx_第14页
第14页 / 共25页
WSAAnsycSelect.docx_第15页
第15页 / 共25页
WSAAnsycSelect.docx_第16页
第16页 / 共25页
WSAAnsycSelect.docx_第17页
第17页 / 共25页
WSAAnsycSelect.docx_第18页
第18页 / 共25页
WSAAnsycSelect.docx_第19页
第19页 / 共25页
WSAAnsycSelect.docx_第20页
第20页 / 共25页
亲,该文档总共25页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

WSAAnsycSelect.docx

《WSAAnsycSelect.docx》由会员分享,可在线阅读,更多相关《WSAAnsycSelect.docx(25页珍藏版)》请在冰点文库上搜索。

WSAAnsycSelect.docx

6.使用异步套接字编写网络聊天室(afxsockinit用到的是1.1版本。

如果要求其它版本需要自己调winsock);

Windows套接字在两种模式下执行I/O操作,阻塞和非阻塞。

在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回程序(将控制权交还给程序)。

而在非阻塞模式下,Winsock函数无论如何都会立即返回.

Windows Sockets为了支持Windows消息驱动机制,使应用程序开发者能够方便地处理网络通信,它对网络事件采用了基于消息的异步存取策略.

Windows Sockets的异步选择函数WSAAsyncSelect()提供了消息机制的网络事件选择,当使用它登记的网络事件发生时,Windows应用程序相应的窗口函数将收到一个消息,消息中指示了发生的网络事件,以及与事件相关的一些信息.

1)加载套接字库,进行版本协商,包含头文件,链接库文件,这次请示的是2.2

版本!

2)在类CChatDlg中增加一个成员变量m_socket,在析构函数中释放这个变量

3)利用WSASocket()创建套接字(数据报类型的UDP型的)

BOOL CWsaChatDlg:

:

InitSocket()

{

 m_socket = WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);

 if(m_socket==INVALID_SOCKET)

 {

  MessageBox("创建套字失败!

");

  return FALSE;

 }

 SOCKADDR_IN SockAddr;

 SockAddr.sin_family = AF_INET;

 SockAddr.sin_port = htons(5500);

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

 int dwren = bind(m_socket,(SOCKADDR*)&SockAddr,sizeof(SOCKADDR));

 if(SOCKET_ERROR==dwren)

 {

  MessageBox("绑定失败!

");

  return FALSE;

 }

 if(SOCKET_ERROR==WSAAsyncSelect(m_socket,m_hWnd,WM_SOCK,FD_READ))

 {

  MessageBox("注册网络读事件失败!

");

  return FALSE;

 }

 return TRUE;

}

4)然后调用WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ)为网络事件定义消息!

此时如果发生FD_READ消息,系统会发送UM_SOCK消息给应用程序!

程序并不会阻塞在这儿了!

  以上是在BOOL CChatDlg:

:

OnInitDialog()完成5)然后完成消息响应!

  头文件中:

#define UM_SOCK  WM_USER+1

   afx_msg void OnSock(WPARAM,LPARAM);

  源文件中:

  ON_MESSAGE(UM_SOCK,OnSock)

  实现消息响应函数:

void CChatDlg:

:

OnSock(WPARAM wParam,LPARAM lParam)

{

switch(LOWORD(lParam))//Wparam附加参数指出那个socek发生事件,Lparam

的低字表示什么事件,高字表示错误代码。

{

case FD_READ:

  WSABUF wsabuf;

  wsabuf.buf=new char[200];

  wsabuf.len=200;

  DWORD dwRead;

  DWORD dwFlag=0;

  SOCKADDR_IN addrFrom;

  int len=sizeof(SOCKADDR);

  CString str;

  CString strTemp;

  HOSTENT *pHost;

  if(SOCKET_ERROR==WSARecvFrom(m_socket,&wsabuf,1,&dwRead,&dwFlag,

      (SOCKADDR*)&addrFrom,&len,NULL,NULL))

  {

   MessageBox("接收数据失败!

");

   return;

  }

  pHost=gethostbyaddr((char*)&addrFrom.sin_addr.S_un.S_addr,4,AF_INET

);

  //str.Format("%s说 :

%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf);

  str.Format("%s说 :

%s",pHost->h_name,wsabuf.buf);

  str+="\r\n";

  GetDlgItemText(IDC_EDIT_RECV,strTemp);

  str+=strTemp;

  SetDlgItemText(IDC_EDIT_RECV,str);

  break;

}

}

6)完成数据发送的功能!

void CChatDlg:

:

OnBtnSend() 

{

// TOD Add your control notification handler code here

DWORD dwIP;

CString strSend; WSABUF wsabuf;

DWORD dwSend;

int len;

CString strHostName;

SOCKADDR_IN addrTo;

HOSTENT* pHost;

if(GetDlgItemText(IDC_EDIT_HOSTNAME,strHostName),strHostName=="")

{

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

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

}

else

{

  pHost=gethostbyname(strHostName);

  addrTo.sin_addr.S_un.S_addr=*((DWORD*)pHost->h_addr_list[0]);

}

addrTo.sin_family=AF_INET;

addrTo.sin_port=htons(6000);

GetDlgItemText(IDC_EDIT_SEND,strSend);

len=strSen

D.GetLength();

wsabuf.buf=strSen

D.GetBuffer(len);

wsabuf.len=len+1;

SetDlgItemText(IDC_EDIT_SEND,"");

if(SOCKET_ERROR==WSASendTo(m_socket,&wsabuf,1,&dwSend,0,

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

{

  MessageBox("发送数据失败!

");

  return;

}}

7)完成将主机名转换为IP地址的功能,以前将IP地址转换为主机名的功能

嘿嘿,由于此程式是采用异步套接字选择机制(非阻塞)使发送端和接收端都在同一单线程里实现!

和阻塞情况下需要采用多线程实现在功能是一样的,并且性能同样出色。

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 对于一个

windows网络编程初学者,下面方法是经典入门。

初学者建议不要用MFC提供的类,而用windows API做一个简单服务器和客户端,这样有助于对socket编程机制的理解。

为了简单起见,应用程序是基于MFC的标准对话框。

Winsock用WINDOWS API实现:

(1)服务器端有两个线程:

主线程 — 你需要编写以下函数来实现 

#define NETWORK_EVENT USER_MESSAGE+100 file:

//定义网络事件 

sockaddr_in clientaddr; file:

//暂时存放客户端IP地址 

file:

//自己定义消息映射函数,将上面定义的网络事件映射到处理函数 

file:

//OnNetEvent为网络事件处理函数,它在下面定义 

ON_MESSAGE(NETWORK_EVENT, OnNetEvent); 

在你对话框中的初始化函数中调用下面的初始化网络的子函数 

BOOL InitNetwork() file:

//初始化网络 

file:

//初始化TCP协议 

BOOL ret = WSAStartup(MAKEWORD(2,2), &wsaData); 

if(ret !

= 0) 

MessageBox("初始化套接字失败!

"); 

return FALSE; 

file:

//创建服务器端套接字 

SOCKET serverSocket 

= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 

if(serverSocket == INVALID_SOCKET) 

MessageBox("创建套接字失败!

"); 

closesocket(m_Socket); 

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(serverSocket ,(const struct sockaddr*)&localaddr, 

sizeof(sockaddr)) == SOCKET_ERROR) 

MessageBox("绑定地址失败!

"); 

closesocket(m_Socket); 

WSACleanup(); 

return FALSE; 

file:

//注册网络异步事件,m_hWnd为应用程序的主对话框或主窗口的句柄 

WSAAsyncSelect(serverSocket, m_hWnd, NETWORK_EVENT, 

FD_ACCEPT | FD_CLOSE | FD_READ | FD_WRITE); 

listen(serverSocket, 5); file:

//设置侦听模式 

return TRUE; 

file:

//定义网络事件的响应函数 

void OnNetEvent(WPARAM wParam, LPARAM lParam) 

file:

//调用API函数,得到网络事件类型 

int iEvent = WSAGETSELECTEVENT(lParam); 

file:

//得到发出此事件的客户端套接字 

SOCKET pSock = (SOCKET)wParam; 

switch(iEvent) 

case FD_ACCEPT:

 file:

//客户端连接请求 

OnAccept(); 

break; 

case FD_CLOSE:

 file:

//客户端断开事件:

 

OnClose(pSock); 

break; 

case FD_READ:

 file:

//网络数据包到达事件 

OnReceive(pSock); 

break; 

case FD_WRITE:

 file:

//发送网络数据事件 

OnSend(pSock); 

break; 

default:

 break; 

void OnAccept(SOCET pSock) file:

//响应客户端连接请求函数 

int len = sizeof(sockaddr); 

file:

//调用API函数,接受连接,并返回一个新套接字 

file:

//还可以获得客户端的IP地址 

SOCKET clientSocket = accept(serverSocket, 

(struct sockaddr*)&clientaddr, &len); 

file:

//为新的socket注册异步事件,注意没有Accept事件 

if(WSAAsyncSelect(clientSocket ,m_hWnd, IP_EVENT, 

FD_CLOSE | FD_READ | FD_WRITE) == SOCKET_ERROR) 

MessageBox("注册异步事件失败!

"); 

return; 

file:

//自编函数,将此客户端的相关信息保存下来:

套接字、

// IP地址、登陆时间 

saveClientSocket(clientSocket,clientAddr,currentTimer); 

void OnClose(SOCET pSock) 

file:

//自编函数,结束与相应的客户端的通信,释放相应资源并做相应处理 

endClientSocket(pSock); 

void OnSend(SOCET pSock) 

file:

//自编函数,在给客户端发数据时做一些预处理 

handleOnSend(pSock); 

void OnReceive(SOCET pSock) 

recv(...); file:

//调用API函数,读出网络缓冲区中的数据包 

file:

//自编函数,将此数据包和发出此数据的客户端 

file:

//clientSocket封装成一条网络消息 

buildNetMsg(...); 

file:

//自编函数,将此网络消息放入一个消息队列中,由工作线程去处理 

saveNetMsg(...); 

SetEvent(...); file:

//用事件对象触发工作线程 

客户端登陆后,随即把自己的计算机名发给服务器,服务器接到后,把它保存下来。

这样服务器就可以显示所有在线客户端的信息了,包括:

客户端计算机名、

IP地址、登陆时间等。

注意:

 客户端没有OnAccept()函数,但有OnConnect()函数。

工作线程 — 

在你的应用程序初始化时,创建并启动一个工作线程 

AfxBeginThread(WorkThread,this,THREAD_PRIORITY_NORMAL); 

file:

//this可能为应用程序的主对话框或主窗口的句柄 

UINT WorkThread(LPVOID pParam) 

while

(1) 

file:

//等待多重事件到来 

int ret = WaitForMultipleObject(...); 

switch(ret) 

case OBJECT_0:

 

if(bNewNetMsg) file:

//查看网络消息队列是否有新的网络消息 

readNetMsg(...); file:

//如有新的网络消息,则读出 

handleNetMsg(...); file:

//处理此网络消息 

break; 

case OBJECT_0 + 1:

 

file:

//做退出处理 

break; 

default:

 break; 

return 0; 

客户端为单线程,登陆服务器时,用connect()函数给服务器发连接请求; 

客户端没有OnAccept()函数,但有OnConnect()函数。

在OnConnect()函数里做发连接请求时的预处理; 

在OnReceive()函数里响应并处理网络数据; 

在OnClose()函数里响应服务器的关闭事件; 

在OnSend()函数里做发数据时的预处理; 

如果你还想实现各客户端之间的在线交流(即所谓的聊天室),你在客户端还可以基于UDP协议 

再做一套多点对多点的局域网组播模型模型,以后在和你聊,你先把上面的程序实现。

以上的I/O异步模型基于Windows的消息机制,另外还可以用事件模型、重叠模型或完成端口模型,

建议你参考Windows网络编程指南之类的书。

如果你能对上面的机制很熟练,你肯定已经对Winsock编网络程序的机制有一定理解,接下来你可以进行更精彩的编程, 不仅可以在网上传输普通数据,而且还 

以传输语音、视频数据,你还可以自己做一个聊天室,和你的同学在实验室的局域网里可以共同分享你的成果。

 2

由于程序是服务器端和客户端是一体的,所以我加了一个BOOL server变量,来判断是服务器端,还是客户端。

点击菜单“文件”->“启动服务器”,则server

=TRUE,表示程序实例是服务器端;反之,server=FALSE,表示程序实例是客户端。

程序对话框在WM_INITDIALOG消息响应中使用WSAStartup初始化

Socket

 

if(WSAStartup(WINSOCK_VERION,&ws))

{

MessageBox(hwnd,"Winsock初始化失败", szDlgTitle,MB_OK|MB_ICONSTOP);

WSACleanup();

return FALSE;

}//初始化 

在WM_CLOSE消息响应中释放Socket:

if(connected_skt !

= INVALID_SOCKET)

{ closesocket(connected_skt); 

}

if( skt !

= INVALID_SOCKET ) 

closesocket(skt); 

if( WSACleanup() !

= 0 ) 

MessageBox(hwnd, "不能释放Winsocket",szDlgTitle,MB_OK ); 

2.1服务器端

监听,按下监听之后调用CreateServer(hwnd)函数。

因为Socket已被初始化,所以这里就创建一个socket:

skt=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

创建成功,接着就绑定创建的socket:

bind(skt,(SOCKADDR*)&addr,sizeof(addr);

绑定成功之后,监听这个socket:

listen(skt,MAX_CONNECTED_NUM );

监听成功之后,就开始选择监听客户端的连接事件:

if( WSAAsyncSelect(skt,hwnd,SOCKETMSG,

FD_ACCEPT) == SOCKET_ERROR )

{

MessageBox(hwnd,"WSAAsyncSelect() 失败", szDlgTitle,MB_OK);

return FALSE;

当有客户端连接的时候,

WSAAsyncSelect(skt,hwnd,SOCKETMSG,FD_ACCEPT)函数拦截到这一事件,函数就向程序发送消息SOCKETMSG(这是一个自定义消息),然后程序处理这一消息:

有客户端连接就接受该连接,并创建一个新的用来与客户端通信的socket:

connected_skt,原来最初创建的socket――skt就继续监听有没有客户端连接事件。

if((connected_skt=accept(skt,(struct sockaddr *)&clientaddr,&Len))== INVALID_SOCKET )

{

MessageBox(hwnd,"接受客户端的Socket连接失败", szDlgTitle,MB_OK);

return FALSE;

}

SetDlgItemText(hwnd,IDC_REVTXT,"已经接受客户端连接");

//连接上了,然后监听客户端的FD_READ和关闭 

WSAAsyncSelect(connected_skt, hwnd,SOCKETMSG,FD_READ|FD_CLOSE);  

 先做服务器端应用程序。

  用MFC向导做一个基于对话框的应用程序SocketSever,注意

第三步中不要选上Windwos Sockets

选项。

在做好工程后,创建一个SeverSock,将它设置为异步非阻塞模式,并为它注册各种网络异步事件,然后与自定义的网络异步事件联系上,最后还要将它设置为监听模式。

在自定义的网络异步事件的回调函数中,你可以得到各种网络异步事件,根据它们的类型,做不同的处理。

下面将详细介绍如何编写相关代码。

  在SocketSeverDlg.h文件的类定义之前增加如下定义:

#define NETWORK_EVENT

WM_USER+166 file:

//定义网络事件SOCKET ServerSock; file:

//

服务器端Socket 

  在类定义中增加如下定义:

class CSocketSeverDlg :

 CDialog

{

 public:

  SOCKET ClientSock[CLNT_MAX_NUM]; file:

//存储与客户端通信的

Socket的数组

  /*各种网络异步事件的处理函数*/

  void OnClose(SOCKET CurSock); file:

//对端Socket断开

  void OnSend(SOCKET CurSock); file:

//发送网络数据包

  void OnReceive(SOCKET CurSock); file:

//网络数据包到达

  void OnAccept(SOCKET CurSock); file:

//客户端连接请求

  BOOL InitNetwork(); file:

//初始化网络函数

  void OnNetEvent(WPARAM wParam, LPARAM lParam); file:

//异步事件回调函数

  …

}; 

  在SocketSeverDlg.cpp文件中增加消息映射,其中OnNetEvent是异步事件回调函数名:

ON_MESSAGE(NETWORK_EVENT,OnNetEvent)

 

  定义初始化网络函数,在SocketSeverDlg.cpp文件的OnInitDialog()中调此函数即可。

BOOL CSocketSeverDlg:

:

InitNetwork()

{

 WSADATA wsaData;

 //初始化TCP协议

 BOOL ret = WSAStartup(MAKEWORD(2,2), &wsaData);

 if(ret !

= 0)

 {

  MessageBox("初始化网络协议失败!

");

  return FALSE;

 }

 //创建服务器端套接字

 ServerSock = socket(AF_IN

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > IT计算机 > 电脑基础知识

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2