免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 8617 | 回复: 5
打印 上一主题 下一主题

linux socket C/C++ 封装类,和服务器守护进程的设计 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-03-14 11:39 |只看该作者 |倒序浏览
最近正在做一个项目  有一个小模块 是做 一个 socket通讯 ,我以前很少做过这方面的工作,不知道自己设计的 c++封装类 是否 合理 。
源代码 后附
我的设计思路是这样的
1.交互协议
首先定义数据传送的格式:
        typedef struct DataHead{
        int MSG;
        int data_total_length;
        int data_package_length;
}DataHead;
上表达式为数据包头的格式 ,其中内容可以根据需要扩展,
        int MSG;  为消息的类型。在发送消息时初始化。
int data_total_length; 为消息的总长 (不包括包头,可为0)为0的话 就没有数据只有包头。
int data_package_length;  为每一次 发送或接受的 包的大小。与total_length 一起配合可以得到整个数据包的接收或者发送的过程。       
发送数据包的过程为 先发送数据包头,包头中的total_length,data_package_length。分别规定了包头后的数据长度, 以及接收或者发送的方法。(如果有的数据包过大 可以分几次发送或者接收 ,每次发送data_package_length 长度)

2.类的使用如下
1.  Client client("172.31.118.46",65000); 构造客户端交互对象

2. 对DataHead  client.m_send_datahead 发送包头进行初试化。

3.  可以使用 client.isconect() 判断是否可以连接成功。

4. 调用 client.sendto(char * date , int total_length)发送数据 如果没有任何数据可以使用 client.sendto(NULL,0); 这样就只发包头过去了。

5.调用client.receive()接受服务器发过来的数据包。返回后 可以从  client. m_recv_datahead 中得到接收到的包头,从 client. m_recvbuf 得到接收到的数据首指针(在堆中) ,从 client. m_recv_length  得到接收数据的长度。

其中可以每一部调用都可以通过 client.isErroe()判断是否有错误产生 ,如有可以通过 client.printerror()打印错误。

我不知道这样做是不是合理 。希望大家给我点指导


Client::Client()
{
        m_port = 0 ;
        m_socket = -1 ;
        bzero(&m_serveraddr,sizeof(m_serveraddr));
        m_recvbuf = NULL;
        m_errorcode = 0 ;

}

//override  constructor
Client::Client(char* ip , int port)
{
        int ip_check = 0 ;
        m_socket =  socket(AF_INET,   SOCK_STREAM,   0);
        if(m_socket <= 0)
        {
                setError(ERROR_SOCKET_CREATE);
        }
        if(port <= 1024 || port > 65535 )
        {
                setError(ERROR_PORT_SETTING);
        }


        bzero(&m_serveraddr,sizeof(m_serveraddr));
        m_serveraddr.sin_family =   AF_INET;
        ip_check = inet_addr(ip);
        m_serveraddr.sin_port = htons(port);
        if(-1 == ip_check)
        {
                setError(ERROR_IP_SETTING);
                return ;
        }
        m_serveraddr.sin_addr.s_addr   =   ip_check;
         
         
}


bool Client::isError()
{
        if(m_errorcode == 0)
        return false;


}

void Client::setError(int code)
{
        m_errorcode = code;
}

void Client::setIp(char* Ip)
{
  
}

void  Client::setPort(long port)
{

}
//the function can be used  to get message form server
//param buf is a char pointer that can store the message which is sent by server
//param n is the maximum length of buf
int Client::receive()
{
        if(m_socket  == -1)
        {
                setError(ERROR_SOCKET_CREATE);
                return -1;
        }

        if( recv(m_socket,(char*)&m_recv_datahead,sizeof(DataHead),0) == sizeof(DataHead))
        {
                int total_length = m_recv_datahead.data_total_length;
                if(total_length == 0 )
                        return 1;                      //without any data
                m_recv_length = total_length;
                m_recvbuf = new char[total_length];
                int package_length = m_recv_datahead.data_package_length;
                int recv_times = total_length/package_length +1;
                int i = 1;
                int length_left = total_length;
                while(i <= recv_times)   //three conditions  1. length < packaget 2.length = package*n  3.length > package
                {
                        if(length_left >= package_length)  // when the data length is bigger or equril than the package length
                        {
                                if(package_length != recv(m_socket, m_recvbuf + (i-1)*package_length ,package_length,0))
                                {
                                        setError(ERROR_RECV_FAIL);
                                        return -1;
                                }
                        }
                        else
                        {
                                if(length_left != 0)
                                if(length_left != recv(m_socket,m_recvbuf + (i-1)*package_length , length_left,0))
                                {
                                        setError(ERROR_RECV_FAIL);
                                        return -1;
                                }
                        }
                        length_left = total_length - (i*package_length);
                        i++;
                }
        }
        else
        {
                setError(ERROR_RECVHEAD_FAIL);
                return -1;
        }
        return 1;
}

void Client::printerror()
{

}


//
int Client::sendto(char* buf , int n)
{
        if(-1 == m_socket )
        {
                setError(ERROR_SOCKET_CREATE);
                return -1;
        }

        if( n < 0)
        {
                setError(ERROR_SENDTO_PARAMS_INVAILD);
                return -1;
        }
        if(buf == NULL && n > 0)
        {
                setError(ERROR_SENDTO_PARAMS_INVAILD);
                return -1;
        }
        m_send_datahead.check_code = 100;
        m_send_datahead.data_total_length = n;
        m_send_datahead.data_package_length = 100;
        m_send_datahead.MSG = 1;
        if( send(m_socket, (char*)&m_send_datahead, sizeof(DataHead),0) == sizeof(DataHead))
        {
                if(n == 0) //if there is no any packages
                        return 1;
                int package_length = m_send_datahead.data_package_length;
                int send_times = n/package_length + 1 ;
                int i = 1;
                int length_left = n;
                while(i <= send_times)
                {
                        if( length_left >= package_length )
                        {
                                if(package_length != send(m_socket, buf + (i-1) * package_length ,package_length , 0))
                                {
                                        setError(ERROR_SEND_FAIL);
                                        return -1;
                                }
                        }
                        else
                        {
                                if(length_left != 0)
                                if(length_left != send(m_socket, buf + (i-1) * package_length ,length_left , 0  ))
                                {      
                                        setError(ERROR_SEND_FAIL);
                                        return -1;
                                }
                        }
                        length_left = n - (i * package_length);
                        i++;
                }
        }
        else
        {
                setError(ERROR_SENDHEAD_FAIL);
                return -1;
        }

        return  1;
}

Client::~Client()
{
        close(m_socket);
        if(m_recvbuf != NULL)
                delete m_recvbuf;

}





//connect to server
bool Client::isConnect()
{
        if(connect(m_socket,(struct sockaddr*)&m_serveraddr,sizeof(struct sockaddr)) == -1)
        {
                return false;
        }
        else
        {
                return true ;
        }

}



谢谢大家!!!

论坛徽章:
0
2 [报告]
发表于 2008-03-14 11:43 |只看该作者
还有服务端的 daemon这样设计的
while (1)
        {  
                newsockfd = accept(sockfd, (struct sockaddr *) &cliaddr, &clilen);
                if (newsockfd < 0 && errno == EINTR)
                        continue;
                else if (newsockfd < 0)
                        errorout("failed to accept connection");
                if ((childpid = fork()) == 0)
                {
                        close(sockfd);
                        process(newsockfd);  //处理 函数
                        exit(0);
               
                }
            
        }

项目的人说 这样不行 得做成非阻塞的 就是 用select  ,而我觉得用fork 也可以
项目是一个网管理控制系统 可以说 没有性能的要求。


谢谢 大家 希望大家多给我指导。

论坛徽章:
0
3 [报告]
发表于 2008-03-14 12:41 |只看该作者
来了一个client,你得到一个newsockfd ,然后你对这个newsockfd 单独处理,用多线程和用多进程都可以

你说的select跟你的用fork根本扯不到什么关系吧?

recv函数默认是阻塞的,假如server端用recv接受数据,不用select监控超时,那么假如client永远不发数据,你不是和这个client的连接就永远不会断开?

论坛徽章:
0
4 [报告]
发表于 2008-03-14 17:33 |只看该作者
谢谢你
但是 select不也要这样吗
for(; {  
        FD_ZERO(&fdR);  
        FD_SET(sockfd, &fdR);  
        switch (select(sockfd + 1, &fdR, NULL, &timeout)) {  
                case -1:  
                        error handled by u;  
                case 0:  
                        timeout hanled by u;  
                default:  
                        if (FD_ISSET(sockfd)) {  
                                now u read or recv something;  
                                /* if sockfd is father and   
                                server socket, u can now  
                                accept() */  
                        }  
        }  
}

recv 是经过 了 accept了的 怎么会阻塞?
你说得对,这个超时问题 真不好处理 。  
我是第一次 写这样的东东
请多多指导

论坛徽章:
0
5 [报告]
发表于 2008-03-25 11:40 |只看该作者
accept 了的为什么就不会阻塞呢?
client没发东西,你recv什么?

论坛徽章:
0
6 [报告]
发表于 2008-03-25 12:24 |只看该作者

回复 #1 ring8595 的帖子

有boost::asio这些现成的library使用,自己封装,受累不讨好
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP