Winsock开发网络通信程序的经典入门

对于许多初学者来说,网络通信程序的开发,普遍的一个现象就是觉得难以入手。许多概念,诸如:同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)等,初学者往往迷惑不清,只知其所以而不知起所以然。

  同步方式指的是发送方不等接收方响应,便接着发下个数据包的通信方式;而异步指发送方发出数据后,等收到接收方发回的响应,才发下一个数据包的通信方式。

  阻塞套接字是指执行此套接字的网络调用时,直到成功才返回,否则一直阻塞在此网络调用上,比如调用recv()函数读取网络缓冲区中的数据,如果没有数据到达,将一直挂在recv()这个函数调用上,直到读到一些数据,此函数调用才返回;而非阻塞套接字是指执行此套接字的网络调用时,不管是否执行成功,都立即返回。比如调用recv()函数读取网络缓冲区中数据,不管是否读到数据都立即返回,而不会一直挂在此函数调用上。在实际Windows网络通信软件开发中,异步非阻塞套接字是用的最多的。平常所说的C/S(客户端/服务器)结构的软件就是异步非阻塞模式的。

  对于这些概念,初学者的理解也许只能似是而非,我将用一个最简单的例子说明异步非阻塞Socket的基本原理和工作机制。目的是让初学者不仅对Socket异步非阻塞的概念有个非常透彻的理解,而且也给他们提供一个用Socket开发网络通信应用程序的快速入门方法。操作系统是Windows 98(或NT4.0),开发工具是Visual C++6.0。

  MFC提供了一个异步类CAsyncSocket,它封装了异步、非阻塞Socket的基本功能,用它做常用的网络通信软件很方便。但它屏蔽了Socket的异步、非阻塞等概念,开发人员无需了解异步、非阻塞Socket的原理和工作机制。因此,建议初学者学习编网络通信程序时,暂且不要用MFC提供的类,而先用Winsock2  API,这样有助于对异步、非阻塞Socket编程机制的理解。

  为了简单起见,服务器端和客户端的应用程序均是基于MFC的标准对话框,网络通信部分基于Winsock2 API实现。

  先做服务器端应用程序。
  用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_INET, SOCK_STREAM, IPPROTO_TCP);
    if(ServerSock == INVALID_SOCKET)
    {
        MessageBox("创建套接字失败!");
        closesocket(ServerSock);
        WSACleanup();
        return FALSE;
    }

//绑定到本地一个端口上
    sockaddr_in localaddr;
    localaddr.sin_family = AF_INET;
    localaddr.sin_port = htons(8888);  //端口号不要与其他应用程序冲突
    localaddr.sin_addr.s_addr = 0;
    if(bind(ServerSock ,(struct sockaddr*)&localaddr,sizeof(sockaddr))
                                          = = SOCKET_ERROR)
    {
        MessageBox("绑定地址失败!");
        closesocket(ServerSock);
        WSACleanup();
        return FALSE;
    }
 
    //将SeverSock设置为异步非阻塞模式,并为它注册各种网络异步事件,其中m_hWnd      
    //为应用程序的主对话框或主窗口的句柄
    if(WSAAsyncSelect(ServerSock, m_hWnd, NETWORK_EVENT, FD_ACCEPT | FD_CLOSE | FD_READ | FD_WRITE) == SOCKET_ERROR)
    {
        MessageBox("注册网络异步事件失败!");
        WSACleanup();
        return FALSE;
    }
    listen(ServerSock, 5); file://设置侦听模式
    return TRUE;
}

下面定义网络异步事件的回调函数

void CSocketSeverDlg::OnNetEvent(WPARAM wParam, LPARAM lParam)
{
    //调用Winsock API函数,得到网络事件类型
    int iEvent = WSAGETSELECTEVENT(lParam);

//调用Winsock API函数,得到发生此事件的客户端套接字
    SOCKET CurSock= (SOCKET)wParam;

switch(iEvent)
    {
        case FD_ACCEPT:      //客户端连接请求事件
            OnAccept(CurSock);
            break;
        case FD_CLOSE:       //客户端断开事件:
            OnClose(CurSock);
            break;
        case FD_READ:        //网络数据包到达事件
            OnReceive(CurSock);
            break;
         case FD_WRITE:      //发送网络数据事件
            OnSend(CurSock);
            break;
         default: break;
     }
}

  以下是发生在相应Socket上的各种网络异步事件的处理函数,其中OnAccept传进来的参数是服务器端创建的套接字,OnClose()、OnReceive()和OnSend()传进来的参数均是服务器端在接受客户端连接时新创建的用与此客户端通信的Socket。

void CSocketSeverDlg::OnAccept(SOCKET CurSock)
{
    //接受连接请求,并保存与发起连接请求的客户端进行通信Socket
    //为新的socket注册异步事件,注意没有Accept事件
}
 
void CSocketSeverDlg::OnClose(SOCET CurSock)
{
    //结束与相应的客户端的通信,释放相应资源
}

void CSocketSeverDlg::OnSend(SOCET CurSock)
{
    //在给客户端发数据时做相关预处理
}

void CSocketSeverDlg::OnReceive(SOCET CurSock)
{
    //读出网络缓冲区中的数据包
}

  用同样的方法建立一个客户端应用程序。初始化网络部分,不需要将套接字设置为监听模式。注册异步事件时,没有FD_ACCEPT,但增加了FD_CONNECT事件,因此没有OnAccept()函数,但增加了OnConnect()函数。向服务器发出连接请求时,使用connect()函数,连接成功后,会响应到OnConnect()函数中。下面是OnConnect()函数的定义,传进来的参数是客户端Socket和服务器端发回来的连接是否成功的标志。

void CSocketClntDlg::OnConnect(SOCKET CurSock, int error)
{
    if(0 = = error)
    {
        if(CurSock = = ClntSock)
        MessageBox("连接服务器成功!");
    }
}

  定义OnReceive()函数,处理网络数据到达事件;
  定义OnSend()函数,处理发送网络数据事件;
  定义OnClose()函数,处理服务器的关闭事件。
            
  以上就是用基于Windows消息机制的异步I/O模型做服务器和客户端应用程序的基本方法。另外还可以用事件模型、重叠模型或完成端口模型,读者可以参考有关书籍。
  在实现了上面的例子后,你将对Winsock编网络通信程序的机制有了一定的了解。接下来你可以进行更精彩的编程, 不仅可以在网上传输普通数据,而且还以传输语音、视频数据,你还可以自己做一个网络资源共享的服务器软件,和你的同学在实验室的局域网里可以共同分享你的成果。

http://www.comprg.com.cn/detail.asp?hw_id=7170

Winsock开发网络通信程序的经典入门相关推荐

  1. VC网络编程-Winsock开发网络通信程序的经典入门

    Winsock开发网络通信程序的经典入门 对于许多初学者来说,网络通信程序的开发,普遍的一个现象就是觉得难以入手.许多概念,诸如:同步(Sync) / 异步(Async),阻塞(Block) / 非阻 ...

  2. MongoDB开发学习开天辟地,经典入门

    如果你从来没有接触MongoDB或对MongoDB有一点了解,如果你是C#开发人员,那么你不妨花几分钟看看本文.本文将一步一步带您轻松入门. 阅读目录 一:简介 二:特点 三:下载安装和开启服务器 四 ...

  3. Android开发的经典入门教材和学习…

    Android开发的经典入门教材和学习路线? 1.想利用寒假期间学习Android开发,了解到应该先学习Java,不知道选哪本书入门,学习Java和Android有什么经典教材,适合初学者.(有C++ ...

  4. 你也可以玩转Skype -- 基于Skype API开发外壳程序入门

    原文:你也可以玩转Skype -- 基于Skype API开发外壳程序入门 Skype是目前这个星球上最厉害的IM+VOIP软件,Skype现在已经改变了全球2.8亿人的生活方式.你,值得拥有! :) ...

  5. Web程序员如何入门以太坊开发

    我经常构建使用以太坊的Web应用程序,我理所当然地认为每天都使用的是神奇的工具集.我们的生态系统正在迅速发展,我认为很多新人都感到不知所措.以太坊是一项了不起的技术,但它也是新生的,而且根本没有足够的 ...

  6. 【微信小程序开发•系列文章一】入门

    本系统文章主要有以下几篇: <[微信小程序开发•系列文章一]入门> <[微信小程序开发•系列文章二]视图层> <[微信小程序开发•系列文章三]数据层> <[微 ...

  7. C#开发语音程序入门之文字朗读

    要想用C#开发语音程序,首先要电脑上要有Speech API(SPAI). 大家通过读这段内容可以了解到自己的电脑是否已经安装SPAI: The Speech API has been an inte ...

  8. 微信小程序开发.小程序入门(上)

    1.小程序简介 微信小程序,小程序的一种,英文名Wechat Mini Program,   是一种不需要下载安装即可使用的应用,   它实现了应用"触手可及"的梦想,用户扫一扫或 ...

  9. Electron前端开发桌面程序--入门篇

    前言 前端开发桌面程序这个概念已经出现有一段时间了,这项技术也已经走向成熟,Github上nw和光electron的star就差不多有10w颗星了,github也衍生出了很多开源的桌面项目俨然成了一个 ...

最新文章

  1. poj3692(二分匹配)
  2. 今天不聊我这些啦,来聊聊我们这种外行对你们的了解吧
  3. 玩转iOS开发:《iOS设计模式 — 单例模式》
  4. 如何新建分支上传_如何创建git分支?
  5. tcp 和 dcp 的几大区别
  6. 大一大学计算机考试难吗,新生必看!大一期间必考的3个证书,不考后悔,越拖越难考!...
  7. 详解API Gateway流控实现,揭开ROMA平台高性能秒级流控的技术细节
  8. php xingnengfenxi_php代码性能分析方法
  9. 我的世界服务器改地皮生态系统,我的世界服务器怎么把地皮世界改为和平模式?...
  10. MVC面试问题与答案
  11. 【实用】Putty常见错误汇总
  12. SQL——后台分页(C#,mysql)
  13. 大数据分析需要掌握哪些技术
  14. UltraEdit 许可证ID 如何加密和解密文本教程分享
  15. linux终端怎么设置monaco,ubuntu16.04安装monaco字体
  16. spring的核心模块
  17. Security登录页面显示:Bad credentials 或者 BCryptPasswordEncoder : Encoded password does not look like BCrypt
  18. Delphi UAC生成默认以管理员身份运行的可执行程序
  19. 元数据管理平台技术白皮书
  20. 51单片机 八音盒设计

热门文章

  1. Docker容器管理总结
  2. wps怎么投递简历发到boss直聘_BOSS直聘情色招聘:洗脑传销广告漫天飞,还陷虚假招聘...
  3. linux系统qt中make安装,Linux系统下Qt的基本安装和配置
  4. php缩紧空格,使用php-cs-fixer修复PHP代码文件的缩进
  5. wxpython 安装_下载和安装wxPython
  6. mysql事务隔离级别与设置
  7. linux查看占用内存最多的程序
  8. Open Train 10394
  9. 《编程珠玑》 读书笔记
  10. java之yield(),sleep(),wait()区别详解