Win32 API 之 socket网络编程

对于许多初学者来说,网络通信程序的开发,普遍的一个现象就是觉得难以入手。许多概念,诸如:同步(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;

file://初始化TCP协议
           BOOL ret = WSAStartup(MAKEWORD(2,2), &wsaData);
          if(ret != 0)
           {
                 MessageBox(‘初始化网络协议失败!‘);
                return FALSE;
           }

file://创建服务器端套接字
           ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if(ServerSock == INVALID_SOCKET)
           {
                MessageBox(‘创建套接字失败!‘);
               closesocket(ServerSock);
               WSACleanup();
                return FALSE;
           }

file://绑定到本地一个端口上
           sockaddr_in localaddr;
           localaddr.sin_family = AF_INET;
           localaddr.sin_port = htons(8888);   file://端口号不要与其他应用程序冲突
           localaddr.sin_addr.s_addr = 0;
          if(bind(ServerSock ,(struct sockaddr*)&localaddr,sizeof(sockaddr))
                                           = = SOCKET_ERROR)
          {
                MessageBox(‘绑定地址失败!‘);
                closesocket(ServerSock);
                WSACleanup();
                return FALSE;
          }

file://将SeverSock设置为异步非阻塞模式,并为它注册各种网络异步事件,其 中    m_hWnd       
          file://为应用程序的主对话框或主窗口的句柄
         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)
      {
         file://调用Winsock API函数,得到网络事件类型
         int iEvent = WSAGETSELECTEVENT(lParam);
           
         file://调用Winsock API函数,得到发生此事件的客户端套接字
         SOCKET CurSock= (SOCKET)wParam;

switch(iEvent)
         {
         case FD_ACCEPT:       file://客户端连接请求事件
                 OnAccept(CurSock);
                 break;
         case FD_CLOSE:        file://客户端断开事件:
                 OnClose(CurSock);
                 break;
         case FD_READ:         file://网络数据包到达事件
                 OnReceive(CurSock);
                 break;
          case FD_WRITE:       file://发送网络数据事件
                 OnSend(CurSock);
                 break;
          default: break;
          }
      }
     
       以下是发生在相应Socket上的各种网络异步事件的处理函数,其中OnAccept传进来的参数是服务器端创建的套接字,OnClose()、OnReceive()和OnSend()传进来的参数均是服务器端在接受客户端连接时新创建的用与此客户端通信的Socket。
     void CSocketSeverDlg::OnAccept(SOCKET CurSock)
     {
          file://接受连接请求,并保存与发起连接请求的客户端进行通信Socket
      file://为新的socket注册异步事件,注意没有Accept事件
     }

void CSocketSeverDlg::OnClose(SOCET CurSock)
     {
         file://结束与相应的客户端的通信,释放相应资源
     }

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

void CSocketSeverDlg::OnReceive(SOCET CurSock)
     {
         file://读出网络缓冲区中的数据包
     }         
         
        用同样的方法建立一个客户端应用程序。初始化网络部分,不需要将套接字设置为监听模式。注册异步事件时,没有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模型做服务器和客户端应用程序的基本方法。另外还可以用事件模型、重叠模型或完成端口模型,读者可以参考有关书籍。

时间: 2024-08-13 05:03:08

Win32 API 之 socket网络编程的相关文章

【linux高级程序设计】(第十三章)Linux Socket网络编程基础 2

BSD Socket网络编程API 创建socket对象 int socket (int __domain, int __type, int __protocol) :成功返回socket文件描述符,失败返回-1. 参数1:socket对象使用的地址簇或协议簇  常用的有PF_LOCAL(本机通信).PF_INET(IPv4协议簇).PF_INET6(IPv6协议簇) 参数2:socket的类型.常见有:面向连接的数据流方式:面向无连接的数据报方式 参数3:标识采用哪一种协议,0表示默认. 绑定

python进阶之Socket 网络编程

 一:网络编程介绍   自从互联网诞生以来,现在基本上所有的程序都是网络程序,很少有单机版的程序了. 计算机网络就是把各个计算机连接到一起,让网络中的计算机可以互相通信.网络编程就是如何在程序中实现两台计算机的通信. 举个例子,当你使用浏览器访问新浪网时,你的计算机就和新浪的某台服务器通过互联网连接起来了,然后,新浪的服务器把网页内容作为数据通过互联网传输到你的电脑上. 由于你的电脑上可能不止浏览器,还有QQ.Skype.Dropbox.邮件客户端等,不同的程序连接的别的计算机也会不同,所以,更

Socket网络编程之概述理解

今天主要讲讲什么是socket网络编程 socketde 英文原义是"孔"或者"插座".是进程通讯的一种方式,即调用这个网络库的一些API函数实现分布在不同主机的相关进程之间的数据交换.通常也被称作"套接字",用关于描述IP地址和端口,是一个通信链的句柄 也类似于电话插座.举个电话网例子:电话的通话双方相当于相互通信的两个程序,电话号码就是IP地址. 任何用户在通话之前,首先要占用一部电话机,相当于申请一个socket:同时要知道对方的号码,相当

Socket网络编程(TCP/IP/端口/类)和实例

Socket网络编程(TCP/IP/端口/类)和实例 原文:C# Socket网络编程精华篇 转自:微冷的雨 我们在讲解Socket编程前,先看几个和Socket编程紧密相关的概念: TCP/IP层次模型 当然这里我们只讨论重要的四层 01,应用层(Application):应用层是个很广泛的概念,有一些基本相同的系统级TCP/IP应用以及应用协议,也有许多的企业应用和互联网应用.http协议在应用层运行. 02,传输层(Tanspot):传输层包括UDP和TCP,UDP几乎不对报文进行检查,而

Socket网络编程进阶与实战

第1章 课程导学(Java语言教学)[说明:课程案例部分以Java语言实现]本章节首先会对课程进行导学讲解,包括为什么应该学习本课程,课程目标与收获,课程内容安排,适合人群和学习建议等,接着会讲解代码规范与开发注意事项,目的全在于希望极大的方便同学进行本课程的学习.... 第2章 Socket网络编程快速入门本章首先整体介绍什么是Socket网络编程:让大家对Socket有个大概的概念与方向.之后通过几个小Case引出课程的主角,轻松愉快的让你体验到Socket编程的快感.本章节主要收获:1.

socket 网络编程快速入门(一)教你编写基于UDP/TCP的服务(客户端)通信

因为UNIX和Win的socket大同小异,为了方便和大众化,这里先介绍Winsock编程. socket 网络编程的难点在入门的时候就是对基本函数的了解和使用,因为这些函数的结构往往比较复杂,参数大部分都是结构体,令人难以记忆和理解. 但是一旦我们知道这些函数包括其参数的具体含义,socket网络编程也就变得不是那么复杂.这里不赘述 具体函数的详细含义,网络上有很多的文章,同时笔者建议大家参考 MSDN,对返回值,参数等会有更好的理解. 以下均为单线程的简单实例,多线程的请关注下一篇文章. (

Socket网络编程--网络爬虫(1)

我们这个系列准备讲一下--网络爬虫.网络爬虫是搜索引擎系统中十分重要的组成部分,它负责从互联网中搜集网页,采集信息,这些网页信息用于建立索引从而为搜索引擎提供支持,它决定着整个引擎系统的内容是否丰富,信息是否即时,因此其性能的优劣直接影响着搜索引擎的效果.网络爬虫的基本工作原理: (1)从一个初始URL集合中挑选一个URL,下载该URL对应的页面: (2)解析该页面,从该页面中抽取出其包含的URL集合,接下来将抽取的URL集合再添加到初始URL集合中: (3)重复前两个过程,直到爬虫达到某种停止

windows下的socket网络编程(入门级)

windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先写了个简单的winSocket网路通信的例子,以便以后用到的时候有个参考. windows下使用winsock编程与linux/unix的区别在于windows下需要先有一个初始化的操作,结束的时候需要一个清理的操作.还有windows下编译的时候需要连接ws32_lib库. 大致过程如下 1.初始

Linux Socket 网络编程

Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后,过段时间不看,重新拾起这些知识的时候又要从头开始,所以,在这里做个笔记也算是做个模板,以后可以直接从某一个阶段开始接着玩... 1. socket套接字介绍 socket机制其实就是包括socket, bind, listen, connect, accept等函数的方法,其通过指定的函数实现不同