首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网络技术 > 网络基础 >

RAII打包TCP连接

2013-11-04 
RAII封装TCP连接文件描述符的分配方式:使用当前最小可用的文件描述符。在多线程环境中文件描述符容易出错:

RAII封装TCP连接

    文件描述符的分配方式:使用当前最小可用的文件描述符。在多线程环境中文件描述符容易出错:若一个线程A持有一个描述符fd,另一个线程B在close(fd)后立即open一个描述符刚好值等于前面的fd,那么线程A拿着fd读写是要出错的,线程不能感知描述符的死活。采用RAII手法封装描述符,对象析构时关闭描述符,只要对象还活着就不会有其它对象和它有一样的描述符。

    给出一个简单粗糙的例子:服务端主线程采用epoll监听端口sockfd可写表明有客户连接请求,此时主线程将创建个子线程处理该连接请求,那么该客户连接的生命周期也应该由子线程管理。

    服务端程序功能:有客户连接,子线程将客户发送的数据打屏

#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<assert.h>#include<stdio.h>#include<unistd.h>#include<errno.h>#include<string.h>#include<fcntl.h>#include<stdlib.h>#include<sys/epoll.h>#include<signal.h>#include<sys/wait.h>#include<sys/mman.h>#include<iostream>using namespace std;class TcpConnection{//客户TCP连接类,采用RAII封装客户连接    public:        TcpConnection(int listenfd){//构造函数中就accept一个客户连接            struct sockaddr_in client;            socklen_t client_addrlength=sizeof(client);            connfd=accept(listenfd,(struct sockaddr*)&client,&client_addrlength);            if(connfd<0)                cout<<"connect error"<<endl;            cout<<"TcpConnection"<<endl;        }        int get(){            return connfd;        }        ~TcpConnection(){//析构时关闭TCP连接            close(connfd);            cout<<"~TcpConnection"<<endl;        }    private:        int connfd;};int setnonblocking(int fd){//设置描述符非阻塞    int old_option=fcntl(fd,F_GETFL);    int new_option=old_option|O_NONBLOCK;    fcntl(fd,F_SETFL,new_option);    return old_option;}void* worker(void* arg){//子线程管理TCP客户连接    int* listenfd=(int*)arg;    TcpConnection one(*listenfd);//accept一个客户连接    int fd=one.get();    char buf[1024];    while(1){        memset(buf,'\0',1024);        int ret=recv(fd,buf,1024-1,0);        if(ret<=0){            cout<<"shutdown the TcpConnection"<<endl;            break;        }        else{            cout<<buf<<endl;//输出客户发送的内容        }    }}void addfd(int epollfd,int fd){//将监听端口加入事件表    epoll_event event;    event.data.fd=fd;    event.events=EPOLLIN;    epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);    setnonblocking(fd);}int main(int argc,char* argv[]){    if(argc<=2){        cout<<"argc<=2"<<endl;        return 1;    }    const char* ip=argv[1];    int port=atoi(argv[2]);    struct sockaddr_in address;    bzero(&address,sizeof(address));    address.sin_family=AF_INET;    inet_pton(AF_INET,ip,&address.sin_addr);    address.sin_port=htons(port);    int sockfd=socket(PF_INET,SOCK_STREAM,0);    assert(sockfd>=0);    int ret=bind(sockfd,(struct sockaddr*)&address,sizeof(address));    assert(ret!=-1);    ret=listen(sockfd,5);    int epollfd=epoll_create(5);    assert(epollfd!=-1);    addfd(epollfd,sockfd);    epoll_event events[65535];    pthread_t pid;    while(1){        int ret=epoll_wait(epollfd,events,65535,-1);        if(ret<0){            cout<<"epoll error"<<endl;            break;        }        for(int i=0;i<ret;i++){            int sock=events[i].data.fd;            if(sock==sockfd){//若有客户连接请求,则创建一个子线程管理该客户连接                pthread_create(&pid,NULL,worker,&sock);            }            else{                cout<<"unknown fd"<<endl;            }        }    }}
客户端telnet服务端端口地址,然后发送数据,服务端将数据打屏

热点排行