TCP流式套接字的阻塞模式编程

时间:2021-02-13 10:21:34

在Windows环境下,套接口的通信方式分为两种:阻塞方式和非阻塞方式。阻塞方式下工作的套接口在进行I/O操作时,函数要等待到相关操作完成以后才能返回(或者可以使用WSACancelBlockingCall( )调用唤起一个阻塞操作)。 阻塞方式的套接口编程简单,易于实现。正因为如此,一个套接口的默认操作模式被设置为阻塞方式。

以下为测试阻塞模式TCP流式套接字编程,一个服务器两个客户端
服务器端程序:

#include < WinSock2.h> 
#pragma comment ( lib, "ws2_32" ) 
#include < stdio.h> 
int main() 
{ 
    printf( "服务器端程序/n" ); 
    //---------------①加载-------------------------
    WSADATA wsaData; 
    WORD wVersionRequested= MAKEWORD( 2 ,2 ); 
    if ( WSAStartup( wVersionRequested,& wsaData)!= 0 )
    { 
        printf( "WSAStartup() Failed,Error=【%d】/n" , WSAGetLastError()); 
        return 1 ;
    } 
    else 
        printf( "①加载成功/n" ); 
    //----------------②创建流式套接字--------------
    SOCKET s= socket( AF_INET, SOCK_STREAM, 0 ); 
    if ( s== INVALID_SOCKET) 
    { 
        printf( "socket() Failed,Error=【%d】/n" , WSAGetLastError()); 
        return 1 ;
    } 
    else 
        printf( "②已创建监听套接口:【%d】/n" , s); 
    //-----------------③绑定本地地址------------------
    struct sockaddr_in Sadd; 
    Sadd.sin_family= AF_INET; 
    Sadd.sin_port= htons( 1111 ); 
    Sadd.sin_addr.s_addr= inet_addr( "192.168.31.1" ); 
    if ( bind( s,( sockaddr*)& Sadd, sizeof ( Sadd))== SOCKET_ERROR) 
    { 
        printf( "bind() Failed,Error=【%d】/n" , WSAGetLastError()); 
        return 1 ;
    } 
    else 
        printf( "③绑定成功,本地IP地址:【%s】,端口号:【%d】/n" , inet_ntoa( Sadd.sin_addr), ntohs( Sadd.sin_port)); 
    //------------------④进入监听状态--------------------
    if ( listen( s, 5 )== SOCKET_ERROR) 
    { 
        printf( "listen() Failed,Error=【%d】/n" , WSAGetLastError()); 
        return 1 ;
    } 
    else 
        printf( "④进入监听状态/n" ); 
    //------------------⑤循环接受客户的连接请求-----------------
    printf( "⑤等待客户连接请求/n/n" ); 
    struct sockaddr_in Cadd; 
    int CaddLen= sizeof ( Cadd); 
    while ( TRUE ) 
    { 
        SOCKET c= accept( s,( sockaddr*)& Cadd,& CaddLen); 
        if ( c== INVALID_SOCKET) 
        { 
            printf( "accept() Failed,Error=【%d】/n" , WSAGetLastError()); 
            return 1 ;
        } 
        else //**************开始发送、接收********************
        { 
            printf( "客户已来,已创建用于本次连接的套接字是:【%d】/n" , c); 
            printf( "客户端IP地址:【%s】,端口号:【%d】/n" , inet_ntoa( Cadd.sin_addr), ntohs( Cadd.sin_port)); 
            while ( 1 )
            { 
                //-------接受-----
                char Rbuf[ 256 ]; 
                memset( Rbuf, 0 ,sizeof ( Rbuf)); 
                int SRecv= recv( c, Rbuf, 256 ,0 ); 
                if ( SRecv== SOCKET_ERROR) 
                { 
                    printf( "recv() Failed,Error=【%d】或客户端非法关闭连接/n" , WSAGetLastError()); 
                    break ; 
                } 
                else if ( SRecv== 0 )
                { 
                    printf( "没接收到任何来自客户端的数据,或者客户端已关闭本次连接!!/n" ); 
                    break ; 
                } 
                else //------正确接收---
                { 
                    printf( "接收到数据:【%s】/n" , Rbuf); 
                    char Sbuf[]= "Hello! I am a server" ; 
                    int isend= send( c, Sbuf, sizeof ( Sbuf), 0 ); 
                    if ( isend== SOCKET_ERROR) 
                    { 
                        printf( "send() Failed,Error=【%d】/n" , WSAGetLastError()); 
                        break ; 
                    } 
                    else if ( isend== 0 )
                    { 
                        printf( "消息发送失败/n" ); 
                        closesocket( c); 
                        break ; 
                    } 
                    else 
                        printf( "给客户信息【%s】已发送/n" , Sbuf); 
                } //end 正确接收
            } //end while2
        } //end 开始接收
        closesocket( c); 
        printf( "关闭本次用于连接的套接字【%d】,和【%s】连接完毕/n/n" , c, inet_ntoa( Cadd.sin_addr)); 
    } //end while 1
    //---------------⑥关闭、释放--------------------
    closesocket( s); 
    WSACleanup(); 
    return 0 ;
}

1号客户端程序:

#include < WinSock2.h> 
#pragma comment( lib, "ws2_32" ) 
#include < stdio.h> 
int main() 
{ 
    printf( "1号客户端程序/n" ); 
    //-----------①加载----------------
    WSADATA wsaData; 
    WORD wVersionRequested= MAKEWORD( 2 ,2 ); 
    if ( WSAStartup( wVersionRequested,& wsaData)!= 0 )
    { 
        printf( "WSAStartup Failed,Error=【%d】/n" , WSAGetLastError()); 
        return 1 ;
    } 
    else 
        printf( "①加载成功/n" ); 
    //-------------②创建流式套接字-----------------
    SOCKET c1= socket( AF_INET, SOCK_STREAM, 0 ); 
    if ( c1== INVALID_SOCKET) 
    { 
        printf( "socket() Failed,Error=【%d】/n" , WSAGetLastError()); 
        return 1 ;
    } 
    else 
        printf( "②已创建连接套接字:【%d】/n" , c1); 
    //-------------绑定地址---------------------
    struct sockaddr_in C1add; 
    C1add.sin_family= AF_INET; 
    C1add.sin_port= htons( 2222 ); 
    C1add.sin_addr.s_addr= inet_addr( "192.168.31.2" ); 
    if ( bind( c1,( sockaddr*)& C1add, sizeof ( C1add))== SOCKET_ERROR) 
    { 
        printf( "bind() Failed,Error=【%d】/n" , WSAGetLastError()); 
        return 1 ;
    } 
    else 
        printf( "绑定成功,本地IP地址:【%s】,端口号:【%d】/n" , inet_ntoa( C1add.sin_addr), ntohs( C1add.sin_port)); 
    //------------③连接请求---------------
    struct sockaddr_in Sadd; 
    Sadd.sin_family= AF_INET; 
    Sadd.sin_port= htons( 1111 ); 
    Sadd.sin_addr.s_addr= inet_addr( "192.168.31.1" ); 
    if ( connect( c1,( sockaddr*)& Sadd, sizeof ( Sadd))==- 1 )
    { 
        printf( "Failed connect(),Error=【%d】/n" , WSAGetLastError()); 
        return 1 ;
    } 
    else //*********连接成功,可以开始发送、接收************
    { 
        printf( "③连接成功,服务器IP地址:【%s】,端口号:【%d】/n" , inet_ntoa( Sadd.sin_addr), ntohs( Sadd.sin_port)); 
        int a; 
        printf( "希望发送数据吗?(键入“1”发送,键入其他值则退出)" ); 
        scanf( "%d" ,& a); 
        while ( a== 1 )
        { 
            //---发送-----
            char S_buf[]= "Hello!I am a client 1" ; 
            int isend= send( c1, S_buf, strlen( S_buf), 0 ); 
            if ( isend== SOCKET_ERROR) 
            { 
                printf( "Failed send(),Error=【%d】,或者服务器意外关闭/n" , WSAGetLastError()); 
                return 1 ;
            } 
            else if ( isend!= 0 )
                printf( "信息【%s】已发送/n" , S_buf); 
            else 
                printf( "信息无法发送给客户端/n" ); 
            //---接收----
            char R_buf[ 256 ]; 
            int RRecv; 
            memset( R_buf, 0 ,sizeof ( R_buf)); 
            RRecv= recv( c1, R_buf, 256 ,0 ); 
            if ( RRecv== SOCKET_ERROR) 
            { 
                printf( "Failed recv(),Error=【%d】/n" , WSAGetLastError()); 
                return 1 ;
            } 
            else if ( RRecv== 0 )
                printf( "无法收到来自服务器的任何数据,或者服务器意外关闭/n" ); 
            else 
            { 
                printf( "接收到来自服务器的数据:【%s】/n" , R_buf); 
                printf( "希望继续发送数据吗?(键入”1“继续发送,键入其他值则退出)" ); 
                scanf( "%d" ,& a); 
                if ( a!= 1 )
                    break ; 
            } 
        } //-------end 结束
        printf( "已选择退出连接/n" ); 
    } //------end 结束
    //-------------------④关闭、释放------------
    closesocket( c1); 
    WSACleanup(); 
    printf( "④与服务器连接完毕/n" ); 
    return 0 ;
}

2号客户端程序与1号的相差无几,主要是提示文字变成“2号客户端”,绑定的IP地址也不一样。
先启动服务器端,再启动1号客户端,接着启动2号客户端,就会看到如图所示:

TCP流式套接字的阻塞模式编程

服务器端只有当1号客户端退出,才能收到2号客户端的通信。