select模型超时的一点理解和问题
dhbo 2005-06-24 12:40:55 一直想学学socket编程,近几天买了电脑才有自己的时间和空间。
我写了一点
#include "stdafx.h"
#include "winsock2.h"
#include "iostream.h"
#pragma comment(lib,"Ws2_32.lib")
#define OK 0
#define FAIL -1
#define MAX_LEN 255
#define SERVER_PORT 8000
#define MAX_LISTEN 5
CRITICAL_SECTION cs_a;
SOCKET s_newclient[MAX_LEN] ={0};
char szSend[MAX_LEN]="send string !" ;
// 写错误日志,方法不好
void WriteErrorLog(char *strerr,const char * filename="c:\\errlog.txt" )
{
char *perr = new char[strlen(strerr)+6] ;
strcpy(perr,strerr) ;
strcat(perr,"\r\n") ;
InitializeCriticalSection(&cs_a) ;
EnterCriticalSection(&cs_a);
FILE *fp = fopen(filename,"a+") ;
fwrite(perr,sizeof(char),strlen(perr),fp) ;
fclose(fp) ;
LeaveCriticalSection(&cs_a) ;
DeleteCriticalSection(&cs_a) ;
delete [] perr;
}
//取得本地的第一个网卡的IP,但是如果出错,不知道该怎么返回:(
in_addr GetLocalAddress()
{
char szname[MAX_LEN] ;
int iret = gethostname(szname,sizeof(szname)) ;
if(iret == SOCKET_ERROR)
{
sprintf(szname,"call gethost name fail,error code :%d",WSAGetLastError()) ;
WriteErrorLog(szname) ;
WSACleanup() ;
// return (in_addr *)0 ;
// 怎么返回一个类似NULL得in_addr
}
struct hostent *phtent =gethostbyname(szname) ;
return *(in_addr *)phtent->h_addr_list[0] ;
}
// 本来打算如果主机能够与client建立多线程的(与每个连接的client建立一个通信线程)
void DoSthWithNewSock(SOCKET s)
{
char szContent[MAX_LEN] ;
int iret = recv(s,szContent,MAX_LEN,0) ;
//如果socket已经关闭
if(iret == 0)
{
closesocket(s);
return;
}
//如果recv失败
else if(iret ==SOCKET_ERROR)
{
sprintf(szContent,"call recv fail,error code :%d",WSAGetLastError()) ;
WriteErrorLog(szContent) ;
WSACleanup() ;
return ;
}
else
{
iret = send(s,szContent,strlen(szContent),0) ;
if(iret ==SOCKET_ERROR )
{
sprintf(szContent,"call send error,error code:%d",WSAGetLastError()) ;
WriteErrorLog(szContent) ;
closesocket(s) ;
WSACleanup() ;
return ;
}
return ;
}
}
int main(int argc, char* argv[])
{
printf("Hello World!\n");
WSADATA wsadata ;
WORD uVersion = MAKEWORD(2,2) ;
int iret = WSAStartup(uVersion,&wsadata) ;
char szerror[MAX_LEN] ;
if(iret!=OK)
{
sprintf(szerror,"WsaStartup call fail,error code : %d",WSAGetLastError()) ;
WriteErrorLog(szerror) ;
WSACleanup() ;
return FAIL ;
}
SOCKET s = socket(AF_INET,SOCK_STREAM,0) ;
if(s==INVALID_SOCKET)
{
sprintf(szerror,"socket call fail ,error code :%d",WSAGetLastError()) ;
WriteErrorLog(szerror) ;
WSACleanup() ;
return FAIL ;
}
sockaddr_in s_sock,s_client ;
s_sock.sin_family = AF_INET ;
s_sock.sin_port =htons(SERVER_PORT) ;
s_sock.sin_addr = GetLocalAddress() ;
iret = bind(s,(sockaddr *)&s_sock,sizeof(sockaddr)) ;
if(iret == SOCKET_ERROR)
{
sprintf(szerror,"bind call fail,error code :%d",WSAGetLastError()) ;
WriteErrorLog(szerror) ;
WSACleanup() ;
return FAIL ;
}
iret = listen(s,MAX_LISTEN) ;
if(iret == SOCKET_ERROR )
{
sprintf(szerror,"listen call fail,error code :%d",WSAGetLastError());
WriteErrorLog(szerror);
WSACleanup() ;
return FAIL ;
}
fd_set fdread,fdwrite ;
FD_ZERO(&fdread);
FD_ZERO(&fdwrite);
FD_SET(s,&fdread) ;
FD_SET(s,&fdwrite) ;
TIMEVAL tmval ;
int c_len =0 ;
tmval.tv_sec = 1 ;
tmval.tv_usec = 0 ;
int iTimeout = 3000 ;
//setsockopt(s,SOL_SOCKET ,SO_SNDTIMEO,(char *)&iTimeout,sizeof(TIMEVAL)) ;
//设置发送超时
//从网上查的,说是设置发送和接收的超时时间
//setsockopt(s,SOL_SOCKET ,SO_RCVTIMEO,(char *)&iTimeout,sizeof(TIMEVAL)) ;
//设置接受超时
//unsigned long ul = 1;
//ioctlsocket(s, FIONBIO, (unsigned long*)&ul); //设置为非阻塞模式
//网上找的,说是设置超时时间为1s
while(1)
{
iret = select(0,&fdread,&fdwrite,NULL,&tmval) ;
switch(iret)
{
case WSANOTINITIALISED:
break;
case WSAEFAULT:
break;
case WSAENETDOWN:
break ;
case WSAEINTR:
break ;
case WSAEINPROGRESS:
break;
case WSAENOTSOCK:
break ;
case WSAEINVAL:
break;
//垃圾代码,为了查看什么时候出现那几个返回i
case 0:
{
sprintf(szerror,"call select out time,error code :%d",WSAGetLastError()) ;
WriteErrorLog(szerror);
break ;
//超时返回,但是超时过后,select就返回10022错误,说是参数错误,为什么?
}
default:
{
sprintf(szerror,"call select fail,error code :%d",WSAGetLastError()) ;
WriteErrorLog(szerror);
closesocket(s) ;
WSACleanup() ;
return FAIL ;
}
}
if(FD_ISSET(s,&fdread))
{
SOCKET newSocket = accept(s,(sockaddr *)&s_client,&c_len) ;
if(newSocket == SOCKET_ERROR)
{
sprintf(szerror,"call accept error,error code : %d",WSAGetLastError()) ;
WriteErrorLog(szerror) ;
closesocket(s) ;
WSACleanup() ;
return FAIL ;
}
DoSthWithNewSock(newSocket) ;
}
if(FD_ISSET(s,&fdwrite))
{
iret = send(s,szSend,strlen(szSend),0) ;
if(iret == SOCKET_ERROR)
{
sprintf(szerror,"call send at main fail,error code :%d",WSAGetLastError()) ;
//closesocket(s) ;
return FAIL;
}
}
}
return 0;
}
以上是我的烂代码,我第一次写点,对select有一点理解是:(看书,没写理解深刻估计)
1)select模型是阻塞模型
它是为了管理多个SOCKET,如果不自己设置超时时间,那么就一直阻塞
但是如果设置了超时时间,难道时间到了,就把原来的SOCKET s 给改变了?否则为什么select没有再次返回0(连接超时),唯一的解释是把服务器端的s给改变了
2)怎么和客户端建立多个连接呢?我想server和client建立多个线程,还没有想到这个的典型的应用是什么?(除了聊天室)
---初学者,欢迎大家扔鸡蛋(闪~~~^_^)