select()函数的作用

第三:timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

返回值:

负值:select错误

正值:某些文件可读写或出错

0:等待超时,没有可读写或错误的文件

在有了select后可以写出像样的网络程序来!举个简单的例子,就是从网络上接受数据写入一个文件中。

---------------------------无连接

例子:

int main()

{

int sock;

FILE* fp;

struct fd_set fds;

struct timeval timeout = {3, 0}; //select 等待3秒,3秒轮询, 要非阻塞就置0

char buffer[256] = {0}; //256字节的接收缓冲区

/*假设已经建立UDP连接,具体过程不写,简单,当然TCP也同理,主机ip和port都已经给定,要写的文件已经打开

sock = socket(…);

bind(…);

fp = fopen(…); */

while(1)

{

FD_ZERO(&fds); //每次循环都要清空,否则不能检测描述符变化

FD_SET(sock, &fds); //添加描述符

FD_SET(fp, &fds); //同上

maxfdp = sock>fp?sock+1:fp+1; //描述符最大值加1

switch(select(maxfdp, &fds, &fds, NULL, &timeout)) //select使用

{

case SOCKET_ERROR: exit(-1); break; //select错误,退出程序

case 0: break; //再次轮询

default:

if(FD_ISSET(sock, &fds)) //测试sock是否可读,即是否网络上有数据

{

recvfrom(sock, buffer, 256, … ); //接受网络数据

if(FD_ISSET(fp, &fds)) //测试文件是否可写

fwrite(fp, buffer…); //写入文件

buffer清空;

} //end if break

} //end switch

} //end while

} //end main

---------------------------面向连接

#include <winsock.h>

#include <stdio.h>

#define PORT       5150

#define MSGSIZE     1024

#pragma comment(lib, “ws2_32.lib”)

int     g_iTotalConn = 0;

SOCKET g_CliSocketArr[FD_SETSIZE];

DWORD WINAPI WorkerThread(LPVOID lpParameter);

int main()

{

WSADATA     wsaData;

SOCKET       sListen, sClient;

SOCKADDR_IN local, client;

int         iaddrSize = sizeof(SOCKADDR_IN);

DWORD       dwThreadId;

// Initialize Windows socket library

WSAStartup(0x0202, &wsaData);

// Create listening socket

sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

// Bind

local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

local.sin_family = AF_INET;

local.sin_port = htons(PORT);

bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));

// Listen   listen(sListen, 3);

// Create worker thread

CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);

while (TRUE)

{               // Accept a connection

sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);

printf(“Accepted client:%s:%d\n”, inet_ntoa(client.sin_addr), ntohs(client.sin_port));

// Add socket to g_CliSocketArr

g_CliSocketArr[g_iTotalConn++] = sClient;

}

return 0;

}

DWORD WINAPI WorkerThread(LPVOID lpParam)

{

int             i;

fd_set         fdread;

int             ret;

struct timeval tv = {1, 0};

char           szMessage[MSGSIZE];

while (TRUE)

{

FD_ZERO(&fdread);

for (i = 0; i < g_iTotalConn; i++)

{

FD_SET(g_CliSocketArr, &fdread);

}                     // We only care read event

ret = select(0, &fdread, NULL, NULL, &tv);

if (ret == 0)

{       // Time expired

continue;

}

for (i = 0; i < g_iTotalConn; i++)

{

if (FD_ISSET(g_CliSocketArr, &fdread))

{         // A read event happened on g_CliSocketArr

ret = recv(g_CliSocketArr, szMessage, MSGSIZE, 0);

if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))

{

// Client socket closed

printf(“Client socket %d closed.\n”, g_CliSocketArr);

closesocket(g_CliSocketArr);

if (i < g_iTotalConn - 1)

{

g_CliSocketArr[i–] = g_CliSocketArr[–g_iTotalConn];

}

}

else

{

// We received a message from client

szMessage[ret] = ‘\0’;

send(g_CliSocketArr, szMessage, strlen(szMessage), 0);

}

} //if

}//for

}//while

return 0;

}

服务器的几个主要动作如下:

1.创建监听套接字,绑定,监听;

2.创建工作者线程;

3.创建一个套接字数组,用来存放当前所有活动的客户端套接字,每accept一个连接就更新一次数组;

4.接受客户端的连接。

这里有一点需要注意的,就是我没有重新定义FD_SETSIZE宏,所以服务器最多支持的并发连接数为64。而且,这里决不能无条件的ccept,服务器应该根据当前的连接数来决定

是否接受来自某个客户端的连接。一种比较好的实现方案就是采用WSAAccept函数,而且让WSAAccept回调自己实现的Condition Function。

如下所示:

int CALLBACK ConditionFunc(LPWSABUF lpCallerId,LPWSABUF lpCallerData, LPQOS lpSQOS,LPQOS lpGQOS,LPWSABUF lpCalleeId, LPWSABUF lpCalleeData,GROUP FAR *

g,DWORD dwCallbackData)

{

if (当前连接数 < FD_SETSIZE)

return CF_ACCEPT;

else

return CF_REJECT;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值