在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号客户端,就会看到如图所示:
服务器端只有当1号客户端退出,才能收到2号客户端的通信。