zoukankan      html  css  js  c++  java
  • 黑客编程教程(四)关于服务器和客户端编程

    第四节 关于服务器和客户端编程
    
    
           在网络编程中,最常用和最基础的就是WINSOCK. 现在我们讨论WINDOWS下的SOCKET编程.
        
            大凡在WIN32平台上的WINSOCK编程都要经过下列步骤:
          定义变量->获得WINDOCK版本->加载WINSOCK库->初始化->创建套接字->设置套接字选项->关闭套接字->卸载WINSOCK库->释放资源
    
        下面介绍WINSOCK C/S的建立过程:
    
        服务器                        客户端
    ________________________________________________
    1  初始化WSA                     1  初始化WSA
     ____________________________________________________
     2  建立一个SOCKET                2  建立一个SOCKET
     _____________________________________________________
     3  绑定SOCKET                    3  连接到服务器
    _____________________________________________________
    4  在指定的端口监听              4  发送和接受数据
    _____________________________________________________
    5  接受一个连接                  5   断开连接
    ______________________________________________________-
    6  发送和接受数据
    ___________________________________________________
    7  断开连接
    __________________________________________________
      
        大家注意,在VC中进行WINSOCK编程时,需要引入如下两个库文件:WINSOCK.H(这个是WINSOCK API的头文件,WIN2K以上支持WINSOCK2,所以
     可以用WINSOCK2.H);Ws2_32.lib(WINSOCK API连接库文件).
    使用方式如下:
                   #include <winsock.h>
            #pragma comment(lib,"ws2_32.lib")
      
       下面我们通过具体的代码演示服务器和客户端的工作流程:
    
     首先,建立一个WSADATA结构,通常用wsaData
      WSADATA wsaData;
    
     然后,调用WSAStartup函数,这个函数是连接应用程序与winsock.dll的第一个调用.其中,第一个参数是WINSOCK 版本号,第二个参数是指向
     WSADATA的指针.该函数返回一个INT型值,通过检查这个值来确定初始化是否成功.调用格式如下:WSAStartup(MAKEWORD(2,2),&wsaData),其中
    MAKEWORD(2,2)表示使用WINSOCK2版本.wsaData用来存储系统传回的关于WINSOCK的资料.
    
    if(iResuit=WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
     {
      printf("WSAStartup failed:%d",GetLastError());  //返回值不等与0,说明初始化失败
            ExitProcess();                                  //退出程序
    }
    
    应用程序在完成对请求的SOCKET库使用后,要调用WSACleanup函数来接触SOCKET库的绑定,并且释放资源.
    
    注意WSAStartup初始化后,必须建立一个SOCKET结构来保存SOCKET句柄.
    
    下面我们建立一个SOCKET.
    
    首先我们建立一个m_socket的SOCKET句柄,接着调用socket()函数,函数返回值保存在m_socket中.我们使用AF_INFE,SOCK_STREAM,IPPROTO_TCP
    三个参数.第一个表示地址族,AF_INFE表示TCP/IP族,第二个表示服务类型,在WINSOCK2中,SOCKET支持以下三种类型;
    
    SOCK_STREAM 流式套接字
    SOCK_DGRAM  数据报套接字
    SOCK_RAW    原始套接字
    
    第三个参数表示协议:
    
    IPPROTO_UDP  UDP协议 用于无连接数据报套接字
    IPPROTO_TCP  TCP协议 用于流式套接字
    IPPROTO_ICMP ICMP协议用于原始套接字
    
    m_socket=socket(AF_INFE,SOCK_STREAM,IPPROTO_TCP);    //创建TCP协议
    
    以下代码用于检查返回值是否有错误:
    
    if(m_scoket==INVALID_SOCKET)
     {
      prinrf("Error at socket():%d
    ",GetLastError());
      WSACleanup();                                   //释放资源
     return;                                  
     }
    说明,如果socket()调用失败,他将返回INVALID_SOCKET.
    
    
         为了服务器能接受一个连接,他必须绑定一个网络地址,下面的代码展示如何绑定一个已经初始化的IP和端口的Socket.客户端程序用这个
    IP地址和端口来连接服务器.
    
    sockaddr_in service;
     service.sin_family=AF_INET;                       //INTERNET地址族
    service.sin_addr.s_addr=inet_addr("127.0.0.1");   //将要绑定的本地IP地址
    service.sin_port=htons(27015);                     //27015将要绑定的端口
    
    
     下面我们调用BIND函数,把SOCKET和SOCKADDR以参数的形式传入,并检查错误.
    
    if(bind(m_socket,(SOCKADDR*)&SERVICE,sizeof(service))==SOCKET_ERROR)
     {
      printf("bind() failed.
    ");
      closesocket(m_socket);
      return;
     }
    
       当绑定完成后,服务器必须建立一个监听队列,以接受客户端的请求.listen()使服务器进入监听状态,该函数调用成功返回0,否则返回
    SOCKET_ERROR.代码如下:
    
    if(listen(m_socket,1)==SOCKET-ERROR)
     {
      printf("error listening on socket.
    ");
     }
    
    服务器端调用完LISTEN()后,如果此时客户端调用CONNECT()函数,服务器端必须在调用ACCEPT().这样服务器和客户端才算正式完成通信程序的
     连接动作.
    
    一旦服务器开始监听,我们就要指定一个句柄来表示利用ACCEPT()函数接受的连接,这个句柄是用来发送和接受数据的表示.建立一个SOCKET句柄
    Socket AcceptSocket 然后利用无限循环来检测是否有连接传入.一但有连接请求,ACCEPT()函数就会被调用,并且返回这次连接的句柄.
    
    printf("waitong for a client to connect...
    ");
     while(1)
     {
      AcceptSocket=SOCKET_ERROR;
      while(AcceptSocket==SOCKET_ERROR)
      {
       AcceptSocket=accept(m_socket,NULL,NULL);
      }
     }
    
    
    下面看客户端端代码:
      
    sockaddr_in clientService;
     clientService.sin_family=AF_INET;                       //INTERNET地址族
    clientService.sin_addr.s_addr=inet_addr("127.0.0.1");   //将要绑定的本地IP地址
    clientService.sin_port=htons(27015);                     //27015将要绑定的端口
    
    下面调用CONNECT()函数:
    
    if ( connect( m_socket, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR)
     {
             printf( "Failed to connect.
    " );
             WSACleanup();
             return;
     }                                                                     //如果调用失败清理退出
                                                                          //调用成功继续读写数据
    
    _________________________________________________________________________________________________
    到这里,服务器和客户端的基本流程介绍完毕,下面我们介绍数据交换.
    
    
    send():
     int send
     {
      SOCKET s,                    //指定发送端套接字
     const char FAR?*buf,         //指明一个存放应用程序要发送的数据的缓冲区
     int len,                     //实际要发送的数据字节数
     int flags                    //一般设置为0
     };
      C/S都用SEND函数向TCP连接的另一端发送数据.
    
    recv():
     int recv
     {
      SOCKET s,                    //指定发送端套接字
     char FAR?*buf,              //指明一个缓冲区 存放RECC受到的数据
     int len,                     //指明BUF的长度
     int flags                    //一般设置为0
    
    };
     C/S都使用RECV函数从TCP连接的另一端接受数据
    
    _______________________________________________________________________________________________
    
    
    下面将完整的程序代码提供如下,大家可直接编译运行
    
    
     首先看客户端的代码:
    
    #include <stdio.h>
     #include <winsock2.h>
     #pragma comment(lib, "ws2_32.lib")
     void main() {
    
                                                                             // 初始化 Winsock.
         WSADATA wsaData;
         int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
         if ( iResult != NO_ERROR )
             printf("Error at WSAStartup()
    ");
    
                                                                                // 建立socket socket.
         SOCKET client;
         client = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
    
        if ( client == INVALID_SOCKET ) {
             printf( "Error at socket(): %ld
    ", WSAGetLastError() );
             WSACleanup();
             return;
         }
    
                                                                                 // 连接到服务器.
         sockaddr_in clientService;
    
        clientService.sin_family = AF_INET;
         clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" );
         clientService.sin_port = htons( 27015 );
    
        if ( connect( client, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR) {
             printf( "Failed to connect.
    " );
             WSACleanup();
             return;
         }
    
                                                                                     // 发送并接收数据.
         int bytesSent;
         int bytesRecv = SOCKET_ERROR;
         char sendbuf[32] = "Client: Sending data.";
         char recvbuf[32] = "";
    
        bytesSent = send( client, sendbuf, strlen(sendbuf), 0 );
         printf( "Bytes Sent: %ld
    ", bytesSent );
    
        while( bytesRecv == SOCKET_ERROR ) {
             bytesRecv = recv( client, recvbuf, 32, 0 );
             if ( bytesRecv == 0 || bytesRecv == WSAECONNRESET ) {
                 printf( "Connection Closed.
    ");
                 break;
             }
             if (bytesRecv < 0)
                 return;
             printf( "Bytes Recv: %ld
    ", bytesRecv );
         }
    
        return;
     }
    
    
    下面是服务器端代码:
    
    #include <stdio.h>
     #include <winsock2.h>
     #pragma comment(lib, "ws2_32.lib")
     void main() {
    
                                                                                  // 初始化
        WSADATA wsaData;
         int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
         if ( iResult != NO_ERROR )
             printf("Error at WSAStartup()
    ");
    
                                                                                    // 建立socket
         SOCKET server;
         server = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
    
        if ( server == INVALID_SOCKET ) {
             printf( "Error at socket(): %ld
    ", WSAGetLastError() );
             WSACleanup();
             return;
         }
    
                                                                                             // 绑定socket
         sockaddr_in service;
    
        service.sin_family = AF_INET;
         service.sin_addr.s_addr = inet_addr( "127.0.0.1" );
         service.sin_port = htons( 27015 );
    
        if ( bind( server, (SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR ) {
             printf( "bind() failed.
    " );
             closesocket(server);
             return;
         }
        
                                                                                   // 监听 socket
         if ( listen( server, 1 ) == SOCKET_ERROR )
             printf( "Error listening on socket.
    ");
    
                                                                                    // 接受连接
        SOCKET AcceptSocket;
    
        printf( "Waiting for a client to connect...
    " );
         while (1) {
             AcceptSocket = SOCKET_ERROR;
             while ( AcceptSocket == SOCKET_ERROR ) {
                 AcceptSocket = accept( server, NULL, NULL );
             }
             printf( "Client Connected.
    ");
             server = AcceptSocket;
             break;
         }
        
                                                                                          // 发送接受数据
        int bytesSent;
         int bytesRecv = SOCKET_ERROR;
         char sendbuf[32] = "Server: Sending Data.";
         char recvbuf[32] = "";
        
         bytesRecv = recv( server, recvbuf, 32, 0 );
         printf( "Bytes Recv: %ld
    ", bytesRecv );
        
         bytesSent = send( server, sendbuf, strlen(sendbuf), 0 );
         printf( "Bytes Sent: %ld
    ", bytesSent );
    
        return;
     }
    本程序仅仅描述了同步的情况!
  • 相关阅读:
    MFC菜单、工具栏和状态栏
    MFC控件
    MFC对话框
    MFC消息映射机制
    性能优化可使用的工具
    常见的内存问题分析思路
    k8s之Probe
    docker swarm
    keepalived配置虚拟ip(vip)实现双机热备以及自动切换主备
    linux安装openjdk1.8
  • 原文地址:https://www.cnblogs.com/rinack/p/3184940.html
Copyright © 2011-2022 走看看