Select模型解析-什么时候可写

 

// 文章出处 精通Windows Sockets网络开发:基于Visual C++实现.pdf

// 服务器一般实现过程

nErrCode = WSAStartup(wVersion, &wsaData);// 初始化socket库

m_sServer = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP);// 创建socket

ioctlsocket(m_sServer,FIONBIO,&ul);// 设置socket为阻塞模式

getsockopt(inSocket, SOL_SOCKET, SO_SNDBUF, (char*) &uiNewSize, &uiRcvBufLen)

setsockopt(inSocket, SOL_SOCKET, SO_SNDBUF, (char*)&uiBuffSize, uiRcvBufLen)

reVal = bind(m_sServer,(sockaddr*)&m_sServerAddr,sizeof(SOCKADDR_IN));// 绑定socket

reVal = listen(m_sServer,SOMAXCONN);// 监听

hReqAndData = CreateThread(NULL, 0,HandleClient, this, 0, &dwThread);// 创建接收客户连接线程

DWORD WINAPICServerDlg::HandleClient(void *pParam);// 接收客户连接线程

int nRet = select(0, &readfd, &writefd,NULL, NULL);// 采用select模型

closesocket(sHost);

WSACleanup();// 卸载socket库

// 客户端一般实现过程

nErrCode = WSAStartup(wVersion, &wsaData);// 初始化socket库

m_sClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// 创建套接字

ioctlsocket(m_sServer,FIONBIO,&ul);// 设置socket为阻塞模式

getsockopt(inSocket, SOL_SOCKET, SO_SNDBUF, (char*) &uiNewSize, &uiRcvBufLen)

setsockopt(inSocket, SOL_SOCKET, SO_SNDBUF, (char*)&uiBuffSize, uiRcvBufLen)

nErrCode = connect(m_sClientSocket,(sockaddr*)&servAddr,sizeof(SOCKADDR_IN));// 连接服务器

closesocket(m_sClientSocket);

WSACleanup();// 卸载socket库

上面是我们写CS模型时,用到的基本函数,这里只是从代码中拷贝出来,函数具体功能以及各参数意义,请详查文档资料。

这里,主要讲下select模型。

int select(
int nfds,
fd_set FAR *readfds,
fd_set FAR *writefds,
fd_set FAR *exceptfds,
const struct timeval FAR *timeout
);

第一个参数不要管,会被系统忽略的。

第二个参数是用来检查套接字可读性,也就说检查套接字上是否有数据可读

第三个参数用来检查数据是否可以发出。

第四个参数是检查是否有带外数据可读取。
第五个参数是用来设置select等待时间
struct timeval {
long tv_sec; // seconds
long tv_usec; // and microseconds
};
如果将这个结构设置为(0,0),如果模式为非阻塞模式,则立即返回,如果为阻塞模式,则一直等待,直到2,3,4参数中有符合条件的情况。

下面例子是在阻塞模式下完成的。

FD_CLR、FD_ISSET、FD_SET、FD_ZERO,四个宏可以查看文档,得到具体用法。

这里主要讲下select模型怎么判断是否可写情况

FD_SET writefd;// 可写套接字集合

FD_ZERO(&writefd); // 清空集合
FD_SET(sClient, &writefd); // 将客户端套接字加入该集合

int nRet = select(0, 0, &writefd, NULL, NULL);// 检查是否有可写套接字存在

这时候,上面的getsockopt函数就很重要了,函数原型:

WINSOCK_API_LINKAGE
int
WSAAPI
getsockopt(
__in SOCKET s,
__in int level,
__in int optname,
__out_bcount(*optlen) char FAR * optval,
__inout int FAR * optlen
);

getsockopt(inSocket, SOL_SOCKET, SO_SNDBUF, (char*) &uiNewSize, &uiRcvBufLen)

其中uiNewSize为套接字inSocket的缓冲区大小。当然我们可以改变缓冲区的大小(setsockopt(inSocket, SOL_SOCKET, SO_SNDBUF, (char*)&uiBuffSize, uiRcvBufLen))

如果writefd中的套接字还有缓冲区可写,那么此时select会立即返回,我们可以通过可写套接字发送数据。

如果writefd中的套接字没有缓冲区可写,那么此时select阻塞

可能有人会不理解(包括我自己以前)

如果我们不使用select模型,用普通方式建立服务器与客户端的连接,那么我们通过连接套接字向另一端发数据会是什么情况(另一端不接收数据)。

while(TRUE)

{

ulReadSend = send(sLinkSocket, cSendBuf, uSendSize, 0);// sLinkSocket套接字,cSendBuf字符数组,uSendSize发送数据大小

sleep(500);

}

我的测试结果是send阻塞前发送的总数据比getsockopt函数获得的缓冲区大小要大,而且当第一次发送很大的数据时,一般都会成功,且返回值为发送值相等

但第二次发送时,如果服务器没有接收,那么就会阻塞在这,当服务器断开,则立即返回SOCKET_ERROR。

所以,我们采取select模型时,服务器端select只监控可读套接字,来接受客户端连接,然后将每个客户端的连接单独处理,select监控连接套接字的可读、可写状态。

可能有理解不当之处,请指正,共同学习。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值