套接字的阻塞模式
WindowsSockets分别提供了套接字模式和套接字I/O模型,可以对一个套接字的行为进行控制。套接字模式用于当一个套接字被调用时,决定调用函数的阻塞行为。套接字模式有阻塞和非阻塞两种工作模式。套接字I/O模型描述了一个应用程序如何对套接字上的I/O进行管理。套接字和套接字I/O模型是两个不同的概念且他们之间是无关的。本章详细介绍套接字阻塞模式。第五章讲解 非阻塞模式,套接字IO模型有(Select模型,WSAAsyncSelect模型,WSAEventSelect模型,重叠I/O模型,完成端口模型)他们分别在第六、七、八、九、十章讲解
阻塞模式
WindowsSockets在阻塞和非阻塞两种模式下执行I/O操作。在阻塞模式下,在I/O操作完成前,执行的操作函数将一直等候而不会立即返回,该函数所在的线程会阻塞在这里。相反,在非阻塞模式下,套接字函数会立即返回,而不管I/O是否完成,该函数所在的线程会继续运行。
在阻塞模式的套接字上,调用任何一个WindowsSockets API都会消耗不确定的等待时间。在调用recv()时,发生在内核中等待数据和复制数据的过程如下:
当调用recv()时,系统首先检查是否有准备好的数据。如果数据没有准备好,系统就处于等待状态。当数据准备好后,将数据从系统缓冲区复制到用户空间,然后该函数返回。在套接应用程序中,当调用recv()时,用户空间未必就已经存在数据,此时recv()函数就是会处在等待状态。
设置套接字的阻塞模式
在使用socket()和WSASocket()创建套接字时,默认的套接字都是阻塞的。这意味着当调用WindowsSocketsAPI不能立即完成时,线程处于等待状态,直到操作完成。
并不是所有的WindowsSocketsAPI以阻塞套接字为参数调用都会发生阻塞。eg:bind(),listen()时,函数会立即返回。这里将可能阻塞套接字的WindowsSocketsAPI调用分为以下4种
- 输入操作:recv(),recvfrom(),WSARecv(),WSARecvfrom()。以阻塞套接字为参数调用这些函数接收数据,如果此时套接字缓冲区内没有数据可读,则调用线程在数据到来前一直睡眠。
- 输出操作:send(),sendto(),WSASend(),WSASendto()。以阻塞套接字为参数调用这些哈思楠发送数据。如果套接字缓冲区没有可用空间,线程会一直睡眠,直到有空间。
- 接收连接:accept(),WSAAcept()。以阻塞套接字为参数调用这些函数,将等待接受对方的连接请求,如果此时没有连接请求,线程就会进入睡眠状态。
- 外出连接:connect(),WSAConnect()。对于TCP连接,客户端以阻塞套接字为参数,调用这些函数向服务器发起连接。该函数在收到服务器的应答前,不会返回。这就意味着TCP连接总会等待至少从客户端到服务器的一次往返的时间。
阻塞模式套接字的优势和不足
使用阻塞模式的套接字开发网络程序比较简单,容易实现。当希望能够理解发送和接收数据,且处理的套接字数量比较少的情况下,使用阻塞模式来开发网络程序比较合适。
阻塞模式套接字的不知表现为,在大量建立好的套接字线程之间进行通信时比较困难。当使用“生产者——消费者”模型开发网络程序时,为每个套接字都分别分配一个读线程,一个处理数据线程和一个用于同步的事件,无疑加大了系统的开销。阻塞模式套接字的最大的缺点是当希望同时处理大量套接字时,将无从下手,扩展性差。
客户端与服务器端相互问候
BlockServer
[cpp] view plain copy print ?
-
-
- #include <stdio.h>
- #include <WinSock2.h>
- #pragma comment(lib,"ws2_32.lib")
- #define BUF_SIZE 64
- SOCKET sServer;
- SOCKET sClient;
- void RecvLine(SOCKET s,char* buf);
- void main()
- {
- char bufRecv[BUF_SIZE];
- char bufSend[BUF_SIZE];
- memset(bufSend,0,BUF_SIZE);
- memset(bufRecv,0,BUF_SIZE);
- sServer=INVALID_SOCKET;
- sClient=INVALID_SOCKET;
-
- WSADATA wsd;
- if (WSAStartup(MAKEWORD(2,2),&wsd) != 0)
- {
- printf("WSAStartup() failed,errno=%d\n",GetLastError());
- return;
- }
- if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2)
- {
- printf("can not find a usabe windwos sockets dll!");
- WSACleanup();
- return;
- }
- if ((sServer=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == INVALID_SOCKET)
- {
- printf("socket() failed,errno=%d\n",WSAGetLastError());
- WSACleanup();
- return;
- }
-
- SOCKADDR_IN addrServ;
- addrServ.sin_family=AF_INET;
- addrServ.sin_addr.S_un.S_addr=INADDR_ANY;
- addrServ.sin_port=htons(5000);
- if (bind(sServer,(SOCKADDR*)&addrServ,sizeof(addrServ)) == SOCKET_ERROR)
- {
- printf("bind() failed,errno=%d\n",WSAGetLastError());
- closesocket(sServer);
- WSACleanup();
- return;
- }
-
- if ((listen(sServer,2)) == SOCKET_ERROR)
- {
- closesocket(sServer);
- WSACleanup();
- return;
- }
- printf("Server succeded!\nWaiting for new clients...\n");
-
- SOCKADDR_IN addrClient;
- int addrClientLen=sizeof(addrClient);
- if ((sClient=accept(sServer,(SOCKADDR*)&addrClient,&addrClientLen)) == INVALID_SOCKET)
- {
- printf("accept() failed,errno=%d\n",WSAGetLastError());
- closesocket(sServer);
- WSACleanup();
- return;
- }
-
- char* pClientIP=inet_ntoa(addrClient.sin_addr);
- u_short clientPort=ntohs(addrClient.sin_port);
- printf("accept a client[%s]:%d\n",pClientIP,clientPort);
-
- RecvLine(sClient,bufRecv);
- printf("recv client info:%s\n",bufRecv);
-
- strcpy(bufSend,"Hello,Client!\n");
- if (send(sClient,bufSend,strlen(bufSend),0) == SOCKET_ERROR)
- {
- printf("send() failed,errno=%d\n",WSAGetLastError());
- closesocket(sClient);
- closesocket(sServer);
- WSACleanup();
- return;
- }
- printf("server exiting...\n");
- closesocket(sClient);
- closesocket(sServer);
- WSACleanup();
- system("pause");
- return;
- }
-
- void RecvLine(SOCKET s,char* buf)
- {
- BOOL bLineEnd=FALSE;
- int nReadLen=0;
- int nDataLen=0;
- while(!bLineEnd)
- {
- if ((nReadLen=recv(s,buf+nDataLen,1,0)) == SOCKET_ERROR)
- {
- printf("recv() failed,errno=%d\n",WSAGetLastError());
- closesocket(s);
- closesocket(sServer);
- WSACleanup();
- return;
- }
- if (nReadLen == 0)
- {
- printf("读数据失败");
- break;
- }
- if ('\n' == *(buf+nDataLen))
- {
- bLineEnd=TRUE;
- }
- else
- {
- nDataLen+=nReadLen;
- }
- }
- }
// BlockServer.cpp : 定义控制台应用程序的入口点。
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#define BUF_SIZE 64
SOCKET sServer;//服务器监听套接字
SOCKET sClient;//接收客户端套接字
void RecvLine(SOCKET s,char* buf); //读取一行数据
void main()
{
char bufRecv[BUF_SIZE];
char bufSend[BUF_SIZE];
memset(bufSend,0,BUF_SIZE);
memset(bufRecv,0,BUF_SIZE);
sServer=INVALID_SOCKET;
sClient=INVALID_SOCKET;
WSADATA wsd;
if (WSAStartup(MAKEWORD(2,2),&wsd) != 0)//初始化Windows Sockets DLL
{
printf("WSAStartup() failed,errno=%d\n",GetLastError());
return;
}
if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2)//确保WinSock DLL支持2.2
{
printf("can not find a usabe windwos sockets dll!");
WSACleanup();
return;
}
if ((sServer=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == INVALID_SOCKET)//创建套接字
{
printf("socket() failed,errno=%d\n",WSAGetLastError());
WSACleanup();
return;
}
//服务器套接字地址
SOCKADDR_IN addrServ;
addrServ.sin_family=AF_INET;
addrServ.sin_addr.S_un.S_addr=INADDR_ANY;//在程序中使用INADDR_ANY值作为网络地址,这意味着网络服务器提供者将使用合适的网络地址进行绑定。
addrServ.sin_port=htons(5000);
if (bind(sServer,(SOCKADDR*)&addrServ,sizeof(addrServ)) == SOCKET_ERROR)//绑定套接字
{
printf("bind() failed,errno=%d\n",WSAGetLastError());
closesocket(sServer);
WSACleanup();
return;
}
if ((listen(sServer,2)) == SOCKET_ERROR)//开始监听
{
closesocket(sServer);
WSACleanup();
return;
}
printf("Server succeded!\nWaiting for new clients...\n");//等待客户端的连接
SOCKADDR_IN addrClient;
int addrClientLen=sizeof(addrClient);
if ((sClient=accept(sServer,(SOCKADDR*)&addrClient,&addrClientLen)) == INVALID_SOCKET)//接受客户端请求
{
printf("accept() failed,errno=%d\n",WSAGetLastError());
closesocket(sServer);
WSACleanup();
return;
}
//显示客户端的IP和端口
char* pClientIP=inet_ntoa(addrClient.sin_addr);
u_short clientPort=ntohs(addrClient.sin_port);
printf("accept a client[%s]:%d\n",pClientIP,clientPort);
RecvLine(sClient,bufRecv);//接收客户端数据
printf("recv client info:%s\n",bufRecv);
strcpy(bufSend,"Hello,Client!\n");
if (send(sClient,bufSend,strlen(bufSend),0) == SOCKET_ERROR)//向客户端发送数据
{
printf("send() failed,errno=%d\n",WSAGetLastError());
closesocket(sClient);
closesocket(sServer);
WSACleanup();
return;
}
printf("server exiting...\n");//显示退出信息
closesocket(sClient);
closesocket(sServer);
WSACleanup();
system("pause");
return;
}
void RecvLine(SOCKET s,char* buf)
{
BOOL bLineEnd=FALSE;//行结束
int nReadLen=0;//读入字节数
int nDataLen=0;//数据长度
while(!bLineEnd)
{
if ((nReadLen=recv(s,buf+nDataLen,1,0)) == SOCKET_ERROR)//每次读取一个字节
{
printf("recv() failed,errno=%d\n",WSAGetLastError());
closesocket(s);
closesocket(sServer);
WSACleanup();
return;
}
if (nReadLen == 0)
{
printf("读数据失败");
break;//跳出循环
}
if ('\n' == *(buf+nDataLen))//换行符
{
bLineEnd=TRUE;//接收数据结束
}
else
{
nDataLen+=nReadLen;//增加数据长度
}
}
}
BlockClient
[cpp] view plain copy print ?
-
-
- #include <WinSock2.h>
- #include <stdio.h>
- #pragma comment(lib,"ws2_32.lib")
- #define BUF_SIZE 64
- void RecvLine(SOCKET s,char* buf);
- void main()
- {
- WSADATA wsd;
- if (WSAStartup(MAKEWORD(2,2),&wsd) != 0)
- {
- printf("WSAStartup() failed! erron=%d\n",GetLastError());
- return;
- }
-
- SOCKET sClient;
- if ((sClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == INVALID_SOCKET)
- {
- printf("socket() failed! errno=%d\n",WSAGetLastError());
- WSACleanup();
- return;
- }
-
-
- hostent* hostEntry;
- char hostname[BUF_SIZE];
- gethostname(hostname,BUF_SIZE);
- if(!(hostEntry=gethostbyname(hostname)))
- {
- printf("gethostbyname() failed,errno=%d\n",GetLastError());
- closesocket(sClient);
- WSACleanup();
- return;
- }
-
-
- SOCKADDR_IN addrServ;
- addrServ.sin_family=AF_INET;
- addrServ.sin_addr =*((LPIN_ADDR)*hostEntry->h_addr_list);
- addrServ.sin_port=htons(5000);
- if (connect(sClient,(SOCKADDR*)&addrServ,sizeof(addrServ)) == SOCKET_ERROR)
- {
- printf("connect() failed! errno=%d\n",WSAGetLastError());
- closesocket(sClient);
- WSACleanup();
- return;
- }
- printf("connect successfully!!\n");
-
-
- char buf[BUF_SIZE];
- ZeroMemory(buf,BUF_SIZE);
- strcpy(buf,"Hello,Server!\n");
- if (send(sClient,buf,strlen(buf),0) == SOCKET_ERROR)
- {
- printf("send() failed! erron=%d\n",WSAGetLastError());
- closesocket(sClient);
- WSACleanup();
- return;
- }
-
- char bufRecv[BUF_SIZE];
- memset(bufRecv,0,BUF_SIZE);
- RecvLine(sClient,bufRecv);
- printf("recv() from server:%s\n",bufRecv);
-
-
- closesocket(sClient);
- WSACleanup();
- system("pause");
- return;
- }
- void RecvLine(SOCKET s,char* buf)
- {
- bool bLineEnd=false;
- int nDataLen=0;
- int nReadLen=0;
- while(!bLineEnd)
- {
- if ((nReadLen=recv(s,buf+nDataLen,1,0)) == SOCKET_ERROR)
- {
- printf("recv() failed,errno=%d\n",WSAGetLastError());
- closesocket(s);
- WSACleanup();
- return;
- }
- if (nReadLen == 0)
- {
- printf("读数据失败");
- break;
- }
- if ('\n' == *(buf+nDataLen))
- {
- bLineEnd=true;
- }
- else
- {
- nDataLen+=nReadLen;
- }
- }
- }
-
-
-
-
-
// BlockClient.cpp : 定义控制台应用程序的入口点。
#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
#define BUF_SIZE 64
void RecvLine(SOCKET s,char* buf);//读取一行数据
void main()
{
WSADATA wsd;
if (WSAStartup(MAKEWORD(2,2),&wsd) != 0)//初始化套接字动态库
{
printf("WSAStartup() failed! erron=%d\n",GetLastError());
return;
}
SOCKET sClient;//服务器套接字
if ((sClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == INVALID_SOCKET)//创建套接字
{
printf("socket() failed! errno=%d\n",WSAGetLastError());
WSACleanup();//释放套接字资源
return;
}
//获取主机的信息
hostent* hostEntry;
char hostname[BUF_SIZE];
gethostname(hostname,BUF_SIZE);
if(!(hostEntry=gethostbyname(hostname)))
{
printf("gethostbyname() failed,errno=%d\n",GetLastError());
closesocket(sClient);//关闭套接字
WSACleanup();
return;
}
//设置服务器地址
SOCKADDR_IN addrServ;
addrServ.sin_family=AF_INET;
addrServ.sin_addr =*((LPIN_ADDR)*hostEntry->h_addr_list);
addrServ.sin_port=htons(5000);
if (connect(sClient,(SOCKADDR*)&addrServ,sizeof(addrServ)) == SOCKET_ERROR)//连接套接字
{
printf("connect() failed! errno=%d\n",WSAGetLastError());
closesocket(sClient);//关闭套接字
WSACleanup();
return;
}
printf("connect successfully!!\n");
//向服务器发送数据
char buf[BUF_SIZE];
ZeroMemory(buf,BUF_SIZE);//<==>memset(buf,0,BUF_SIZE);
strcpy(buf,"Hello,Server!\n");
if (send(sClient,buf,strlen(buf),0) == SOCKET_ERROR)
{
printf("send() failed! erron=%d\n",WSAGetLastError());
closesocket(sClient);
WSACleanup();
return;
}
char bufRecv[BUF_SIZE];
memset(bufRecv,0,BUF_SIZE);//bufRecv需要置空,不然会出现多余的字符串输出
RecvLine(sClient,bufRecv);
printf("recv() from server:%s\n",bufRecv);
//退出
closesocket(sClient);
WSACleanup();
system("pause");
return;
}
void RecvLine(SOCKET s,char* buf)
{
bool bLineEnd=false;//行结束
int nDataLen=0;//数据长度
int nReadLen=0;//读入字节长度
while(!bLineEnd)
{
if ((nReadLen=recv(s,buf+nDataLen,1,0)) == SOCKET_ERROR)//每次接收一个字节
{
printf("recv() failed,errno=%d\n",WSAGetLastError());
closesocket(s);
WSACleanup();
return;
}
if (nReadLen == 0)
{
printf("读数据失败");
break;//退出循环
}
if ('\n' == *(buf+nDataLen))//换行符
{
bLineEnd=true;//接收数据结束
}
else
{
nDataLen+=nReadLen;//增加数据长度
}
}
}
/*
1.gethostname()函数
int gethostname(__out_bcount(namelen) char FAR * name,IN int namelen);//获取主机的名字
参数:name:用于保存主机名字的字符缓冲区地址
2.gethostbyname()函数
struct hostent FAR * gethostbyname(__in const char FAR * name);//根据主机名称获取主机的网络地址
struct hostent {//表示主机的名称和网络地址
char FAR * h_name; //* official name of host * 主机正式名称
char FAR * FAR * h_aliases; /* alias list *主机别名
short h_addrtype; /* host address type *地址类型
short h_length; /* length of address *地址长度(用字节表示)
char FAR * FAR * h_addr_list; /* list of addresses *主机地址列表
#define h_addr h_addr_list[0] /* address, for backward compat *
};
unsigned long addr=inet_addr("127.0.0.1");
hostent host=gethostbyaddr((char*)&addr,sizeof(addr),2);
*/
结果图
本文转自:http://blog.csdn.net/ouyangshima/article/details/8967847