一、套接字的阻塞模式
Windows Sockets 套接字模式用于当一个套接字被调用时,决定调用函数的阻塞行为。套接字模式有阻塞和非阻塞两种工作模式。
在阻塞模式下,在I/O操作完成之前,执行的操作函数将一直等候而不会立即返回,该函数所在的线程会阻塞在这里。
例如当调用recv()函数时,系统首先检查是否有准备好的数据。如果数据没有准备好,系统就处于等待状态。当数据准备好后,将数据从系统缓冲区复制到用户空间,然后该函数返回。因为在Sockets 应用程序中,当调用recv()函数时,用户空间未必就已经存在数据,此时recv()函数就会阻塞在那里。
二、可能阻塞套接字的API调用
当使用socket()函数和WSASocket()函数创建socket时,默认的socket都是阻塞的。
但是并不是所有的Windows Sockets API 以阻塞套接字为参数都会发生阻塞。例如,以阻塞套接字为参数调用bind()、listen()等函数时,会立即返回。
可能阻塞套接字的Windows Sockets API 调用分为以下4种:
1. 输入操作
recv()、recvfrom()、WSARecv()和WSARecvfrom()函数。
2. 输出操作
send()、sendto()、WSASend()和WSASendto()函数。
3. 接受连接
accept()和WSAAccept()函数。
4. 外出连接
connect()和WSAConnect()函数。
三、阻塞模式套接字的优势和不足
优势:简单、容易实现。
不足:在大量建立好的套接字线程之间进行通信比较困难,无法同时处理大量套接字。
四、实例
以一个服务器和客户端相互问候的C/S程序为例。
1. 服务器实现
#include <iostream>
#include <Winsock2.h>
using namespace std;
int main()
{
//初始化Windows Sockets 动态库
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
{
cout<<"找不到可使用的WinSock dll!"<<endl;
return 1;
}
//创建套接字
SOCKET sListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sListen==INVALID_SOCKET)
{
cout<<"创建socket失败!"<<endl;
WSACleanup();
return 1;
}
//绑定套接字
SOCKADDR_IN addrServ;
addrServ.sin_family=AF_INET;
addrServ.sin_port=htons(5811);
addrServ.sin_addr.S_un.S_addr=INADDR_ANY;
if(bind(sListen,(sockaddr *)&addrServ,sizeof(sockaddr))==SOCKET_ERROR)
{
cout<<"绑定socket失败!"<<endl;
closesocket(sListen);
WSACleanup();
return 1;
}
//监听套接字
if(listen(sListen,10)==SOCKET_ERROR)
{
cout<<"监听socket失败!"<<endl;
closesocket(sListen);
WSACleanup();
return 1;
}
cout<<"服务器启动成功!"<<endl;
cout<<"正在等待客户端连接..."<<endl;
while(1)
{
//接受客户端连接
SOCKADDR_IN addrClient;
int addrlen=sizeof(SOCKADDR_IN);
memset(&addrClient,0,sizeof(SOCKADDR_IN));
SOCKET clientSock=accept(sListen,(sockaddr *)&addrClient,&addrlen);
if(clientSock==INVALID_SOCKET)
{
cout<<"接受socket连接出错!"<<WSAGetLastError()<<endl;
continue;
}
//输出客户端信息
char *strClientAddr=inet_ntoa(addrClient.sin_addr);
unsigned short port=ntohs(addrClient.sin_port);
cout<<"客户端IP:"<<strClientAddr<<",端口号:"<<port<<endl;
//发送数据
int retval=send(clientSock,"Hello,Client!",strlen("Hello,Client!"),0);
if(retval==SOCKET_ERROR)
{
cout<<"发送数据失败!"<<endl;
}
//接收数据
char buf[20];
memset(buf,0,20);
retval=recv(clientSock,buf,20,0);
if(retval==SOCKET_ERROR)
{
cout<<"接收数据失败!"<<endl;
}
cout<<buf<<endl;
}
//关闭套接字,释放资源
closesocket(sListen);
WSACleanup();
return 0;
}
2. 客户端实现
#include <iostream>
#include <Winsock2.h>
using namespace std;
int main()
{
//初始化Windows Sockets 动态库
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
{
cout<<"找不到可使用的WinSock dll!"<<endl;
return 1;
}
//创建套接字
SOCKET sClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sClient==INVALID_SOCKET)
{
cout<<"创建客户端socket失败!"<<endl;
return 1;
}
//连接服务器
SOCKADDR_IN addrServ;
addrServ.sin_family=AF_INET;
addrServ.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addrServ.sin_port=htons(5811);
if(connect(sClient,(sockaddr *)&addrServ,sizeof(sockaddr))==SOCKET_ERROR)
{
cout<<"连接服务器失败!"<<endl;
closesocket(sClient);
return 1;
}
else
cout<<"连接服务器成功!"<<endl;
//发送数据
int retval=send(sClient,"Hello,Server!",strlen("Hello,Server!"),0);
if(retval==SOCKET_ERROR)
{
cout<<"发送数据失败!"<<endl;
}
//接收数据
char buf[20];
memset(buf,0,20);
retval=recv(sClient,buf,20,0);
if(retval==SOCKET_ERROR)
{
cout<<"接收数据失败!"<<endl;
}
cout<<buf<<endl;
//关闭套接字,释放资源
closesocket(sClient);
WSACleanup();
return 0;
}
【本文系原创,转载请注明出处】
阅读全文
类别:visual c++ 查看评论
posted on 2010-10-14 16:57
ewook 阅读(237)
评论(0) 编辑 收藏 引用