VC++ 网络编程总结(一)

1、套接字编程原理

        一个完整的网间通信进程需要由两个进程组成,并且只能用同一种高层协议。也就是说,不可能通信的一段用TCP,而另一端用UDP。一个完整的网络信息需要一个五元组来标识:协议、本地地址、本地端口号、远端地址、远端端口号。

1.1Client/Server通信模型

        在客户端/服务器模式中我们将请求服务的一方成为客户,将提供某种服务的一方称为服务器(Server)

       一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说服务进程一直处于休眠状态,直到一个客户对这个服务的地址提出了连接请求。在这个时刻,服务程序被“惊醒”,并且为客户提供服务----对客户的请求做出适当的反应。虽然基于链接的服务是设计客户机/服务器应用程序时的标准,但有些服务也是可以通过无连接的接口提供的。

         客户及/服务器的请求/响应过程示意图如下所示:

                 2013-08-18_094539

1.2Windows Sockets规范

        Windows Sockets规范从90年代的1.0版本开始,经过不断的完善和发展,目前已经有了Windows Sockets 2版本。值得注意的是,Microsoft的MFC库现在只支持Windows Sockets1版本,不支持WindowsSockets2版本。

        MFC提供了两个类用以封装Windows Sockets API。一个是CAsyncSocket类,它主要是提供给那些具有一定网络编程经验,希望同时拥有Socket API编程的灵活性和类库编程便利性的开发者。另一个是CSocket类,它有CAsyncSocket类派生,它具有更高的抽象化,致力于简化网络编程所需的操作。

1.3套接字

1.3.1套接字定义

      套接字是一个通信终结点,它是Sockets应用程序用来在网络上发送或接收数据包的对象。套接字具有类型,与正在运行的进程相关联,并且可以有名称。目前,套接字一般只与使用网际协议组的同一“通信域”中德其他套接字交换数据。使用套接字的应用程序间通信模型如图:

                2013-08-18_095755

1.3.2分类

   1.3.2.1流式套接字

       流式套接字提供没有记录边界的数据流,即字节流。字节流能确保以正确的顺序无重复地被送达。

2013-08-18_100404

1.3.2.2数据报套接字

        数据报套接字支持面向记录的数据流,但不能确保能被送达,也无法确保按照发送顺序或不重复。

       2013-08-19_172014

     “有序”指数据包按发送的顺序送达。“不重复”指一个特定的数据包只能获取一次。这两种套接字都是双向,是可以同时在两个方向上(全双工)进行通信的数据流.

       注意:在某些网络协议下(如XNS),流可以面向记录,即作为记录流而非字节流。但在更常用的TCP/IP协议下,流为字节流,Win Sockets提供与基础协议无关的抽象化级别。

1.3.3套接字的作用

      套接字的作用非常大,至少在下面三种通信上下文中如此:

  • 客户端/服务器模型
  • 对等网络方案,如聊天应用程序
  • 通过让接受应用程序将消息解释为函数调用来进行远程过程调用

1.3.4 端口与地址

     在网络上,一个套接字的标识主要借助于地址和端口来描述。

     套接字的地址指该套接字所在计算机的网络地址,可以为域名或IP地址的形式。通常创建套接字时不必指明网络地址,只有在拥有多个网络地址的机器时,才需要显式指定的一个网络地址。

      同一机器上可以运行多个网络应用程序,每个应用程序都有自己的套接字用以进行网络通信,此时如果只有地址表示套接字,则当一个通信包到达机器时,将无法确定究竟是哪个应用程序的套接字需要接收此信息。由此增加了端口的概念,以协助区分同一机器上不同应用程序的套接字。

      段开口用于标识进程,同一机器上不同的网络应用程序各有不同的端口,这样,通过“网络地址+端口号”的标识方法,便唯一标识了机器上的应用程序了。

实例应用程序:

======客户端

#include < WINSOCK2.H> 
#pragma comment( lib, "WS2_32" ) 
#include < stdio.h> 

int main() 
{ 
     printf( "------------------------\n| 客户端 |\n|---------------------------------------\n" ); 
     //------①加载动态链接库winsock DLL-----------
     printf( "|加载等待中.... " ); 
     WSADATA wsaData; 
     WORD wVersionRequested= MAKEWORD( 2 ,2 ); 
     if ( WSAStartup( wVersionRequested,& wsaData)!= 0 )
     { 
         printf( "|WSAStartup Failed\n" ); 
         printf( "|WSAStartup Error=%d\n" , WSAGetLastError()); 
     } 
     else 
     { 
         printf( "加载Winsock 库成功 |\n" ); 
     } 
     printf( "|---------------------------------------\n" ); 
     //-------②创建用于监听的流式套接口s,使用socket()-----------------
     SOCKET s= socket( AF_INET, SOCK_STREAM, IPPROTO_TCP); 
     if ( s== INVALID_SOCKET) 
     { 
         printf( "|Failed socket\n" ); 
         printf( "|socket Error=%d\n" , WSAGetLastError()); 
     } 
     else 
         printf( "|已创建用于监听的套接口,套接口号:[%u]\n" , s); 
     printf( "|---------------------------------------\n" ); 
     //------③本地地址bind(可以不做这部分,如果不绑定,系统将自动分配)--------
     /*struct sockaddr_in Cadd;
     Cadd.sin_family=AF_INET;
     Cadd.sin_port=htons(4444);
     Cadd.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
     if (bind(s,(sockaddr*)&Cadd,sizeof(Cadd))==SOCKET_ERROR)
     {
         printf("|Failed bind()/n");
     }*/ 
     //-------填写要连接的服务器地址信息---------
     struct sockaddr_in Sadd; 
     Sadd.sin_family= AF_INET; 
     Sadd.sin_port= htons( 5555 ); 
     Sadd.sin_addr.S_un.S_addr= inet_addr( "127.0.0.1" ); 
     //--------④将套接口s与远程主机相连--------------
     if ( connect( s,( sockaddr*)& Sadd, sizeof ( Sadd))== INVALID_SOCKET) 
     { 
         printf( "|Failed connect()\n" ); 
         printf( "|connect Error=%d\n" , WSAGetLastError()); 
     } 
     else 
     { 
         //####################开始发接数据########################
         printf( "|连接成功,可以开始发送接收数据了!\n" ); 
         printf( "|服务器IP地址:[%s]\n 端口号:[%u]\n" , inet_ntoa( Sadd.sin_addr), ntohs( Sadd.sin_port)); 
         //####################结束发接数据########################
     } 
     //--------------⑤关闭套接字s,终止对动态链接库的访问----------
     closesocket( s); 
     printf( "|---------------------------------------\n" ); 
     printf( "|连接完毕\n" ); 
     WSACleanup(); 
     return 0 ;
}

========服务端

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32")

int main(int argc, char* argv[])
{
    printf("---------\n| 服务端 |\n-----------");
    //----------1、加载动态链接库Winsock Dll---------
    WORD wVersionRequested = MAKEWORD(2,2);
    WSADATA wsaData;
    if(WSAStartup(wVersionRequested,&wsaData) != 0)
    {
        printf("WSAStartup Failed \n");
        printf("WSAStartup Error = %d \n",WSAGetLastError());
    }
    else
    {
        printf("加载WinSocket成功 \n");
        printf("调用者希望使用的WinSocket版本号=%x\n",wsaData.wVersion);
        printf("加载的WinSock库支持最高WinSock版本号=%x \n",wsaData.wHighVersion);
        printf("wWinSock库的说明字符串=%s \n",&wsaData.szDescription[0]);
        printf("系统状态或配置信息的说明字符串=%s\n",&wsaData.szSystemStatus[0]);
    }
    //----------2、创建监听的流式套接口----------
    
    SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(s == INVALID_SOCKET)
    {
        printf("| Failed socket\n");
        printf("| Socket Error = %d\n",WSAGetLastError());
    }
    else
        printf("毅创建用于监听的套接口,套接口号:[%u]\n",s);

    //----3、绑定使用bind()
    struct sockaddr_in Sadd;
    Sadd.sin_family = AF_INET;
    Sadd.sin_port = htons(5555);
    Sadd.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    if(bind(s,(sockaddr*)&Sadd,sizeof(Sadd)) == SOCKET_ERROR)
    {
        printf("Failed bind()\n");
        printf("Bind Error = %d\n",WSAGetLastError());
    }
    else
    {
        printf("绑定成功\n");
        printf("本地Ip地址:[%s],本地端口号:[%u]\n",inet_ntoa(Sadd.sin_addr),ntohs(Sadd.sin_port));
    }
    //---------------④监听状态-------------
     if ( listen( s, 2 )== SOCKET_ERROR) 
     { 
         printf( "Failed listen()\n" ); 
         printf( "listen Error=%d\n" , WSAGetLastError()); 
     } 
     //----------------⑤循环接受客户的连接请求---------------------------
     struct sockaddr_in Cadd; 
     int caddLen= sizeof ( Cadd); 
     SOCKET c; 
     while ( TRUE ) 
     { 
         printf( "|----------------------------\n" ); 
         printf( "|进入监听状态.....\n|--------------------------------|\n" ); 
         c= accept( s,( sockaddr*)& Cadd,& caddLen); 
         if ( c== INVALID_SOCKET) 
         { 
             printf( "|Failed accept()\n" ); 
             printf( "|accept Error=%d\n" , WSAGetLastError()); 
         } 
         else 
             printf( "|可以在套接口[%u]上发送接收数据了!\n" , c); 
         //#########################开始发送、接收###################### 注意都要在新套接口c上进行
         //#########################结束发送、接收######################
         closesocket( c); 
         printf( "|与主机IP地址是:[%s]\n|端口号是:[%u]的连接完毕\n" , inet_ntoa( Cadd.sin_addr), ntohs( Cadd.sin_port)); 
         char xx; 
         printf( "|-------------------------------------\n需要退出吗?(Y\n)" ); 
         scanf( "%c" ,& xx); 
         if ( xx== 'Y' || xx== 'y' ) 
         { 
             break ; 
         } 
     } 
     closesocket( s); 
     WSACleanup();
    return 0;
}

运行结果:

2013-08-19_1742062013-08-19_174227

posted @ 2013-08-19 17:44  记忆斑驳的时光  阅读(4142)  评论(1编辑  收藏  举报