Linux c++ select 示例

//主程序
#include <unistd.h>  
#include <iostream>  
#include <sys/socket.h>
#include <sys/select.h>  
#include <arpa/inet.h> 
#include <stdio.h>
#include <string.h>

#include "socket.h"


int main() 
{ 
    int s32ServerSocket = -1; 

    InitServerSocket(s32ServerSocket);
 
     
    //select模型处理过程 
    fd_set  stCheckFdSet,stBakFdSet; 
    //s32AssignFdMax用于记录已分配socket的最大值
	int s32AssignFdMax = s32ServerSocket+1;
	struct timeval stTimeout;
	
	FD_ZERO(&stCheckFdSet);
    FD_ZERO(&stBakFdSet);
    FD_SET(s32ServerSocket, &stBakFdSet);
	
    while (1) 
    { 
       stTimeout.tv_sec = 100;   
       stTimeout.tv_usec = 500000; 
	   //stCheckFdSet 是一个输入输出参数
	   //输入时包含用户感兴趣的描述符
	   //输出时包含活跃的描述符(用户输入集合中的描述符)
	   //所以每次select都要设置fd_set集合
	   stCheckFdSet = stBakFdSet;
        int nRet = ::select(s32AssignFdMax, &stCheckFdSet, NULL, NULL, &stTimeout); 
        if (nRet > 0) 
        { 
            INFO_PRINT("\n"); 
			int s32CurrentFd=0;
            for (s32CurrentFd=0;s32CurrentFd < s32AssignFdMax+1;s32CurrentFd++) 
            { 
                if (FD_ISSET(s32CurrentFd, &stCheckFdSet))//活跃的描述符
                { 
					if(s32ServerSocket == s32CurrentFd)//监听的描述符
					{
						sockaddr_in addrRemote = {0}; 
						socklen_t nAddrLen = sizeof(addrRemote); 
						
						socklen_t NewSocket = ::accept(s32ServerSocket, (sockaddr*)&addrRemote, &nAddrLen); 
						FD_SET(NewSocket, &stBakFdSet); 
						
						if(s32AssignFdMax < NewSocket)
						{
							s32AssignFdMax = NewSocket+1;
						}
						
						INFO_PRINT("accept [%d]!\n",NewSocket); 
					} 
					#if 1
					else  //可读的 
					{ 
						char szContent[256]={0}; 
						
						int nRecv = ::recv(s32CurrentFd, szContent, sizeof(szContent), 0); 
						if (nRecv > 0) 
						{ 
							szContent[nRecv] = '\0'; 
							INFO_PRINT("recv %d:%s\n",nRecv, szContent); 
							::send(s32CurrentFd, szContent, nRecv, 0);  
						} 
						else //异常或关闭的
						{ 
							::close(s32CurrentFd); 
							FD_CLR(s32CurrentFd, &stBakFdSet); 
							INFO_PRINT("close[%d]\n",s32CurrentFd);
						} 
					}
					#endif
				} 
            }
        } 
        else if (nRet == 0)
        { 
            INFO_PRINT("stTimeout!\n"); 
        } 
		else
		{
			 INFO_PRINT("select err!\n"); 
			return -1;; 
		}
    } 
    ::close(s32ServerSocket); //关闭监听描述符
    INFO_PRINT("game over!"); 

    return SUCCESS;
}


//socket部分

#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>


#include "socket.h"



static int gs_s32IdleFd = -1;
int InitErrDeal()
{
	/*忽略pipe信号,当客户端关闭socket时,server第一write时会收到RST segment(tcp 传输层)
	server第二次write时,会收到SIGPIPE(brokenpipe)信号,默认处理是退出
	
	TIME_WAIT状态,先close的一端会进入 time_wait状态,内核会在一定的时间内保留socket资源
	(服务器socket通信设计时,让客户端先close)
	*/
	signal(SIGPIPE, SIG_IGN);
	//避免僵尸进程
	signal(SIGCHLD, SIG_IGN);

	gs_s32IdleFd = open("/dev/null", O_RDONLY | O_CLOEXEC);

	return SUCCESS;
}

int ProcessErr(const ERR_INFO_S &stErrInfo)
{
	switch ( stErrInfo.enErrType)
	{

	    case ERR_EMFILE:
	    {
			int *s32pAcceptFd = (int *)(stErrInfo.pvUserData);
			close(gs_s32IdleFd);
			gs_s32IdleFd = accept(*s32pAcceptFd, NULL, NULL);
			close(gs_s32IdleFd);
			gs_s32IdleFd = open("/dev/null", O_RDONLY | O_CLOEXEC);
	        break;
	    }
	    default:
	    {
	        break;
	    }
	}
	
	return SUCCESS;
}

int InitServerSocket(int &s32ServerFd)
{
	int s32Ret = -1;
	//SOCK_NONBLOCK 设置成非阻塞模式,类似于fcnt的F_SETL 的 O_NONBLOCK	
	//SOCK_CLOEXEC,类似于fcnt 的F_GETFD 的 FD_CLOEXEC ,设置进程替换(fork)时,socket处于关闭状态 
	s32ServerFd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
	if ( s32ServerFd < 0)
	{
		SHOW_ERR("\n");
		return FAILURE;
	}

	struct sockaddr_in stServerAddr;
	memset(&stServerAddr, 0, sizeof(stServerAddr));
	stServerAddr.sin_family = AF_INET;
	stServerAddr.sin_port = htons(SERVER_PORT);
	stServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	//设置地址的可重复利用,用于socket的快速重启
	int s32OnOff = 1;
	s32Ret= setsockopt(s32ServerFd, SOL_SOCKET, SO_REUSEADDR, &s32OnOff, sizeof(s32OnOff));
	if (s32Ret < 0)
	{
		SHOW_ERR("\n");
		return FAILURE;
	}

	s32Ret = bind(s32ServerFd, (struct sockaddr*)&stServerAddr, sizeof(stServerAddr));
	if (s32Ret < 0)
	{
		SHOW_ERR("\n");
		return FAILURE;
	}

	s32Ret = listen(s32ServerFd, SOMAXCONN);
	if (s32Ret < 0)
	{
		SHOW_ERR("\n");
		return FAILURE;
	}
	return SUCCESS;
	
}

int InitClientSocket(int &s32ClientFd)
{
	int s32Ret = FAILURE;

	s32ClientFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

	if (s32ClientFd < 0)
	{
		SHOW_ERR("\n");
	}

	struct sockaddr_in stServerAddr;
	memset(&stServerAddr, 0, sizeof(stServerAddr));
	stServerAddr.sin_family = AF_INET;
	stServerAddr.sin_port = htons(SERVER_PORT);
	stServerAddr.sin_addr.s_addr = inet_addr(SERVER_IP);

	s32Ret = connect(s32ClientFd, (struct sockaddr*)&stServerAddr, sizeof(stServerAddr));
	if (s32Ret < 0)
	{
		SHOW_ERR("\n");
		return s32Ret;
	}

	struct sockaddr_in stLocalAddr;
	socklen_t addrlen = sizeof(stLocalAddr);
	s32Ret = getsockname(s32ClientFd, (struct sockaddr*)&stLocalAddr, &addrlen);
	if (s32Ret < 0)
	{
		SHOW_ERR("\n");
		return s32Ret;
	}

	SHOW_SOCKADDR_INFO(stLocalAddr);

	return SUCCESS;

}

//客户端
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include <iostream>

#include "socket.h"

int main(void)
{
	int s32Socket=-1;

	InitClientSocket(s32Socket);
	
	char acSendBuf[1024] = {0};
	char acRecvBuf[1024] ={0};
	
	while (fgets(acSendBuf, sizeof(acSendBuf), stdin) != NULL)
	{
		::send(s32Socket, acSendBuf, strlen(acSendBuf), 0);
		::recv(s32Socket, acRecvBuf, sizeof(acRecvBuf), 0); 

		fputs(acRecvBuf, stdout);
		memset(acSendBuf, 0, sizeof(acSendBuf));
		memset(acRecvBuf, 0, sizeof(acRecvBuf));
	}

	close(s32Socket);
	
	return 0;
}




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。下面详细介绍一下! Select的函数格式(我所说的是Unix系统下的伯克利socket编程,和windows下的有区别,一会儿说明): int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); 先说明两个结构体: 第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set *),将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。一会儿举例说明。 第二,struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。 具体解释select的参数: int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。 fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。 fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。 fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。 struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。 返回值: 负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值