使用epoll来进行异步的 TCP 连接

长话短说了 。。TCP 连接成功后, 连接的 socket 变成可写状态,那么我们就监听  EPOLLOUT 事件,来判断是不是连接成功了。但是我们需要先调用 connect 但是调用 connect 的时候会阻塞,好吧 还需要先把 socket 设置成非阻塞,这样调用完 connect 里面在线程中进入对 epoll 事件的查询。对于 connect 失败的情况,返回的事件是 EPOLLERR .简单的测试代码:

#include <iostream>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;

typedef void (* PF_CALLBACK)(int fd);

class CEpoll
{
public:
   CEpoll(PF_CALLBACK pfOnRead,PF_CALLBACK pfOnWrite,PF_CALLBACK pfOnError)
   {
       m_epoll = epoll_create(100);//
       if(m_epoll == -1)
       {
           throw "error create epoll ~";
       }

       m_OnRead = pfOnRead;
       m_OnWrite = pfOnWrite;
	   m_OnError = pfOnError;
   }

   ~CEpoll()
   {
       if(m_epoll >= 0)
       {
           close(m_epoll);
       }
   }

public:

	bool AddFdOnWrite(int fd)
	{
	   struct epoll_event ev;
       ev.data.fd = fd;
       //ev.data.ptr = "i am sincoder";//
       ev.events = EPOLLOUT|EPOLLET;

       int ret = epoll_ctl(m_epoll,EPOLL_CTL_ADD,fd,&ev);
       if(ret == -1)
       {
           return false;
       }
       return true;
	}

   bool AddFdOnRead(int fd)
   {

       struct epoll_event ev;
       ev.data.fd = fd;
       //ev.data.ptr = "i am sincoder";//
       ev.events = EPOLLIN|EPOLLET;

       int ret =epoll_ctl(m_epoll,EPOLL_CTL_ADD,fd,&ev);
       if(ret == -1)
       {
           return false;
       }
       return true;
   }

   bool ProcessMsg()
   {
       int i;
       int nr_events = 0;
       int ret;
       char buff[1024];
       struct epoll_event events[64];

           printf("enter wait !\n");
           nr_events = epoll_wait (m_epoll,events,64,-1);
           printf("out wait%d\n",nr_events);

           if (nr_events < 0)
           {
               perror("epoll_wait");
               return false;
           }

           for (i = 0; i < nr_events; i++)
           {
               printf("event=%ld on fd=%d \n",events[i].events, events[i].data.fd);
			   if(events[i].events & EPOLLIN )
			   {
				   m_OnRead(events[i].data.fd);
			   }
			   if(events[i].events & EPOLLOUT )
			   {
				   m_OnWrite(events[i].data.fd);
			   }
			   if(events[i].events & EPOLLERR )
			   {
				   m_OnError(events[i].data.fd);
			   }
           }
       return true;
   }

private:
   int m_epoll;
   PF_CALLBACK m_OnRead;
   PF_CALLBACK m_OnWrite;
   PF_CALLBACK m_OnError;
};

void OnRead(int fd);
int client_socket;
CEpoll *g_epoll = NULL;

void OnRead(int fd)
{
   printf("enter call back ~\n");
   if(fd == client_socket){

   }else{
       char buff[1024];
       int ret = recv(fd,buff,sizeof(buff),0);
       if(ret <= 0)
       {
           printf("close fd %d \n",fd);
           close(fd);
       }
       else{
           printf("recv %d bytes from fd %d \n",ret,fd);
       }
   }
}

void OnWrite(int fd)
{
	printf("enter OnWrite \n");
	//看看 socket 是不是链接成功了 
	int result;
	socklen_t result_len = sizeof(result);
	if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &result, &result_len) < 0) {
		// error, fail somehow, close socket
		close(fd);
		return ;
	}

	if (result != 0) {
    // connection failed; error code is in ‘result‘

		printf("connect failed ~!! \n");
		close(fd);
		return ;
	}

	printf("connect OK ~!! \n");

	//将 fd 设置成非阻塞 并重新加入到 epoll  中
}

void OnError(int fd)
{
	if(fd == client_socket)
	{
		//connect error ~!
		printf("connect Error ~! \n");
		close(fd);
		return;
	}
}

int main(int argc,char **argv) {
   if(argc < 2 )
   {
       printf("usage : <ip> <port> \n");
       return 0;
   }

   struct sockaddr_in serv_addr;
   client_socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
   if(client_socket == -1)
   {
       perror("socket():");
       return -1;
   }

   try {
       g_epoll = new CEpoll(OnRead,OnWrite ,OnError);
   }catch(...)
   {
       close(client_socket);
       return -1;
   }

   printf("get socket fd %d", client_socket);
   bzero((char *) &serv_addr, sizeof(serv_addr));
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = inet_addr(argv[1]) ;
   serv_addr.sin_port = htons(atoi(argv[2]));

   //设置 connect 的 socket 为非阻塞

   long on = 1L;
   if (ioctl(client_socket, (int)FIONBIO, (char *)&on))
   {
       printf("ioctl FIONBIO call failed\n");
   }
   
   g_epoll->AddFdOnWrite(client_socket);

   if (connect(client_socket, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
	   if(errno != EINPROGRESS)
	   {
			printf("%s", "ERROR connecting");
	   }
       //close(client_socket);
       //throw "can not connect to remote";
   }

   while(g_epoll->ProcessMsg());
   return 0;
}
时间: 2024-10-12 22:58:35

使用epoll来进行异步的 TCP 连接的相关文章

服务器后台TCP连接存活问题

0. 背景 公司的服务器后台部署在某一个地方,接入的是用户的APP,而该地方的网络信号较差,导致了服务器后台在运行一段时间后用户无法接入,那边的同事反馈使用netstat查看系统,存在较多的TCP连接. 1. 问题分析 首先在公司内部测试服务器上部署,使用LoadRunner做压力测试,能正常运行,然后那边的同事反馈该地方信号较差.考虑到接入的问题,有可能接入进程的FD资源耗尽,导致accept失败.推论的依据是对于TCP连接来说,如果客户端那边由于一些异常情况导致断网而未能向服务器发起FIN关

配置开发支持高并发TCP连接的Linux应用程序全攻略

http://blog.chinaunix.net/uid-20733992-id-3447120.html http://blog.chinaunix.net/space.php?uid=16480950&do=blog&id=103598 原文见:http://www.cppblog.com/flashboy/articles/47012.html1.修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到

Linux配置支持高并发TCP连接(socket最大连接数)

Linux配置支持高并发TCP连接(socket最大连接数)及优化内核参数 2011-08-09 15:20:58|  分类:LNMP&&LAMP|  标签:内核调优  文件系统调优  高并发调优  socket连接  ip_conntract  |字号大中小 订阅 1.修改用户进程可打开文件数限制在 Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统为每个TCP连接都要创建一个s

TCP连接的关闭

原文地址:http://lib.csdn.net/article/computernetworks/17264 TCP连接的关闭有两个方法close和shutdown,这篇文章将尽量精简的说明它们分别做了些什么. 为方便阅读,我们可以带着以下5个问题来阅读本文: 1.当socket被多进程或者多线程共享时,关闭连接时有何区别? 2.关连接时,若连接上有来自对端的还未处理的消息,会怎么处理? 3.关连接时,若连接上有本进程待发送却未来得及发送出的消息,又会怎么处理? 4.so_linger这个功能

网络编程释疑之:TCP连接拔掉网线后会发生什么

背景:前些天团队在进行终端设备和服务器端长连接业务的测试时,发现了这么一个情况:在拔掉设备端的网线后,再插上网线,有时可以继续正常的进行长接连请求,而且用的还是拔掉网线之前的那个长连接.但是有时却不能继续正常的长连接请求,需要重新建立一个新的长连接.让我尤感诧异的是第一种网线断开再插上后长连接可以恢复的情况,彻底颠覆了我一直抱有的一个所谓的"物理连接"的观念.究竟怎么回事,我们来探个究竟. 首先说说我自己发明的"物理连接"这个名词,不管怎么说我都是一个网络编程的&q

支持并发的http客户端(基于tcp连接池以及netty)

闲来无事,将以前自己写的一个库放出来吧.. 有的时候会有这样子的需求: (1)服务器A通过HTTP协议来访问服务器B (2)服务器A可能会并发的像B发送很多HTTP请求 类似于上述的需求,可能并不常见...因为在业务中确实遇到了这样子的场景,所以就自己动手开发了一个库... 实现原理: (1)底层IO通过netty搞 (2)维护一个tcp的长连接池,这样子就不用每次发送请求还要建立一个tcp连接了... 下面直接来看怎么用吧: (1)最常规的用法,向www.baidu.com发送100次get请

server后台TCP连接存活问题

公司的server后台部署在某一个地方,接入的是用户的APP,而该地方的网络信号较差,导致了server后台在执行一段时间后用户无法接入,那边的同事反馈使用netstat查看系统.存在较多的TCP连接. 1. 问题分析 首先在公司内部測试server上部署,使用LoadRunner做压力測试,能正常执行,然后那边的同事反馈该地方信号较差.考虑到接入的问题.有可能接入进程的FD资源耗尽,导致accept失败.推论的根据是对于TCP连接来说,假设client那边因为一些异常情况导致断网而未能向ser

如何在socket编程的Tcp连接中实现心跳协议

心跳包的发送,通常有两种技术 方法1:应用层自己实现的心跳包 由应用程序自己发送心跳包来检测连接是否正常,大致的方法是:服务器在一个 Timer事件中定时 向客户端发送一个短小精悍的数据包,然后启动一个低级别的线程,在该线程中不断检测客户端的回应, 如果在一定时间内没有收到客户端的回应,即认为客户端已经掉线:同样,如果客户端在一定时间内没 有收到服务器的心跳包,则认为连接不可用. 方法2:TCP的KeepAlive保活机制 因为要考虑到一个服务器通常会连接多个客户端,因此由用户在应用层自己实现心

TCP连接的状态详解以及故障排查

转载自CSDN博客:http://blog.csdn.net/hguisu/article/details/38700899 TCP状态 TCP状态迁移路线图 TCP连接建立三次握手 TCP连接的终止四次握手释放 同时打开 同时关闭 TCP通信中服务器处理客户端意外断开 Linux错误信息errno列表 我们通过了解TCP各个状态,可以排除和定位网络或系统故障时大有帮助.(总结网络上的内容) 1.TCP状态 了解TCP之前,先了解几个命令:   linux查看tcp的状态命令: 1).netst