非阻塞connect实现代码

非阻塞connect实现代码

使用非阻塞IO连接服务端,可将sleep替换要执行的任务,客户主循环利用epoll监听事标准输入和socket上的事件,利用管道将标准输入写入socket
有个问题请教大家,为啥我用splice将通信文件描述符内容拷贝到STDOUT_FILENO会失败,报错无效的输出


```cpp
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/select.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <memory>
#include <sys/time.h>
#include <sys/epoll.h>

using namespace std;

//文件描述符的管理类
class FDHolder{
	public:
		FDHolder(int fd):_fd(new int(fd)){
		}

		~FDHolder(){
			close(*_fd);
		}

		int get(){
			return *_fd;
		}

	private:
		shared_ptr<int> _fd;
};

int setnonblocking(int fd){
	int old_flag = fcntl(fd,F_GETFL);
	fcntl(fd,F_SETFL,old_flag|O_NONBLOCK);
	return old_flag;
}

//非阻塞connect,成功时返回文件描述符,失败时返回-1
int unblock_connect(int fd,const char* ip,int port){
	//设置服务端的信息
	struct sockaddr_in ser;
	bzero(&ser,sizeof(ser));
	ser.sin_family = AF_INET;
	ser.sin_port = htons(port);
	inet_pton(AF_INET,ip,&(ser.sin_addr.s_addr));

	//设置fd为非阻塞状态
	int old_flag = setnonblocking(fd);

	//调用connect查看链接是否成功,如果失败且错误不等于EINPROGRESS,则链接失败
	int ret = connect(fd,reinterpret_cast<struct sockaddr*>(&ser),sizeof(ser));
	if(ret == 0){
		cout << "connect success" << endl;
		return fd;
	}else if(errno != EINPROGRESS)
		return -1;

	//做自己的事
	sleep(5);

	//设置监听写文件描述符集
	fd_set wrfds;
	FD_ZERO(&wrfds);
	FD_SET(fd,&wrfds);
	//设置超时时间
	struct timeval tv;
	tv.tv_sec = 2;
	tv.tv_usec = 0;

	//调用select查看是否已经连接成功
	ret = select(fd+1,nullptr,&wrfds,nullptr,&tv);
	if(ret < 0)
		return -1;
	else if(ret == 0){
		cerr << "连接超时" << endl;
		return -1;
	}

	//如果没有事件,则失败
	if(FD_ISSET(fd,&wrfds) == false){
		cerr << "没有事件发生" << endl;
		return -1;
	}

	//获取错误信息
	int error = 0;
	socklen_t error_len = sizeof(error);
	if(getsockopt(fd,SOL_SOCKET,SO_ERROR,&error,&error_len) < 0){
		cerr << "getsockopt faield" << endl;
		return -1;
	}

	//如果error不为0,则链接失败
	if(error != 0){
		cerr << "Error: " << strerror(error) << endl;
		return -1;
	}

	//连接成功则将文件描述符设回原状态并返回
	cout << "connect success" << endl;
	fcntl(fd,F_SETFL,old_flag);
	return fd;
}

int main(int argc,char* argv[])
{
	if(argc <= 2){
		cerr << "参数太少" << endl;
		return -1;
	}

	const char* ip = argv[1];
	int port = atoi(argv[2]);

	FDHolder hfd(socket(AF_INET,SOCK_STREAM,0));
	if(hfd.get() < 0){
		perror("socket failed");
		return -1;
	}

	//非阻塞链接
	int ret = unblock_connect(hfd.get(),ip,port);
	if(ret < 0){
		cerr << "connect failed" << endl;
		return -1;
	}

	int psock[2];
	ret = pipe(psock);
	if(ret < 0){
		perror("pipe failed");
		return -1;
	}

	//利用epoll监听标准输入和客户端发来的
	int epfd = epoll_create(2);
	if(epfd < 0){
		perror("epoll_create failed");
		return -1;
	}

	//将标准输入和通信描述符上树
	struct epoll_event ev;
	ev.events = EPOLLIN|EPOLLET;
	ev.data.fd = STDIN_FILENO;
	epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&ev);
	ev.data.fd = hfd.get();
	epoll_ctl(epfd,EPOLL_CTL_ADD,hfd.get(),&ev);

	//成功则收发数据
	char buf[1024];
	struct epoll_event events[2];
	while(1){
		int nready = epoll_wait(epfd,events,2,-1);
		if(nready < 0){
			perror("epoll_wait failed");
			return -1;
		}

		for(int i = 0;i < nready;i++){
			if(events[i].data.fd == STDIN_FILENO){
				//将管道接在标准输入和文件描述符两端
				splice(STDIN_FILENO,nullptr,psock[1],nullptr,35536,SPLICE_F_MORE|SPLICE_F_MOVE);
				splice(psock[0],nullptr,hfd.get(),nullptr,35536,SPLICE_F_MOVE|SPLICE_F_MORE);
			}else{
				memset(buf,0x00,sizeof(buf));
				int n = read(hfd.get(),buf,sizeof(buf));
				if(n < 0){
					perror("read failed ");
					return -1;
				}else if(n == 0){
					cout << "server close" << endl;
					return 0;
				}
				cout << "server message: " << buf << endl;
			}
		}
	}

	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值