一个Socket包装类的问题

在以前编写SOCKET包装类的时候,碰到了一个不解的问题,在CSDN论坛上也多亏了各位大虾的帮忙,从而得到了解决。今天重新翻看到,觉得这是一个有价值的问题,所以整理发布在这里。

问题是这样的:

请问,对socket进行包装,其中一个方法是接受客户端的连接。以下的两个函数,一个有问题,一个没问题。请帮忙看看!  
  1、这个函数是没问题的,把对象用引用传进去  
  void   CDGSocket::AcceptClient(CDGSocket   &s)  
 
{  
  struct   sockaddr_in   cliAddrTmp;  
  int   cliAddrSize   =   sizeof(struct   sockaddr_in);  
  SOCKET   sClient   =   accept(m_hSocket,   (struct   sockaddr   *)&cliAddrTmp,   &cliAddrSize);  
  if(sClient   ==   INVALID_SOCKET)  
  {  
  ThrowException("accept()   failed!");  
  }  
   
  s.m_hSocket   =   sClient;  
  }
 
   
  客户程序的调用如下:  
  CDGSocket   sClient;  
  sServer.AcceptClient(sClient);  
   
  2、这个函数是有问题的  
 
CDGSocket   CDGSocket::AcceptClient()  
  {  
  CDGSocket   sClientObj;  
   
  struct   sockaddr_in   cliAddrTmp;  
  int   cliAddrSize   =   sizeof(struct   sockaddr_in);  
  sClientObj.m_hSocket   =   accept(m_hSocket,   (struct   sockaddr   *)&cliAddrTmp,   &cliAddrSize);  
  if(sClientObj.m_hSocket   ==   INVALID_SOCKET)  
  {  
  ThrowException("accept()   failed!");  
  }  
   
  return   sClientObj;  
  }
 
  客户程序是这样调用的:  
  CDGSocket   sClient;  
  sClient   =   sServer.AcceptClient();  
  但是就在返回的时候程序出现了异常,我非常迷惑这里是什么原因造成的。  
   
  我的拷贝构造函数是这样的,里面没有其他的成员变量,也没有动态开辟的空间。  
  按道理来说应该不需要拷贝构造函数。  
  CDGSocket::CDGSocket(const   CDGSocket   &s)  
  {  
  m_hSocket   =   s.m_hSocket;  
  }  
   
  我自己尝试解决所做的试验:  
  1、跟踪出错函数的时候程序是这样跑的:  
  return   sClientObj;-->   拷贝构造函数--〉析构函数--〉返回--〉出错  
   
  开始猜想可能是拷贝构造函数的问题,但是试了一下其他的拷贝内存的方法,也没什么用。  
   
  2、查看了MSDN中SOCKET的解释,发现,SOCKET其实就是unsigned   int,是一个由Windows分配的句柄值,有点类似于HWND,这种的变量的赋值是否有点特别。  

问题分析:

从上面两个方法可以看出来,一个是把接受到的连接放在引用中返回,一个是在返回值中返回。在返回值中返回的对象会以这样一种方式执行,调用拷贝构造函数把值拷贝到外部--〉调用析构函数把原来的值析构掉--〉返回。

大虾们的帮助:

healer_kx(甘草(楼主揭贴吧,我们这些上班灌水的也不容易))回复于 2006-07-21 16:50:46 得分 30

你不是Socket方面的问题,你是C++问题上的错误。  
   
  Socket是不应该具有拷贝构造语意的对象,  
   
  也就是说,不要写出  
  CDGSocket   sClientObj;  
  return   sClientObj;  
  这样的代码。  
   
  这能意味着什么呢?  
   
  就是说,一个Socket对象,它本身具有了某种功能后,你把它拷贝给了别的对象?  
  这个是不行的。   
 

2 楼healer_kx(甘草(楼主揭贴吧,我们这些上班灌水的也不容易))回复于 2006-07-21 16:54:21 得分 0

有些类型的对象,是不应该具有这样的语意的。某位大侠教我的,切记切记。   
 

3 楼sinall()回复于 2006-07-21 17:16:16 得分 30

把你的CDGSocket声明和CDGSocket构造、析构函数拿出来看看。  
  怀疑你的~CDGSocket()里有关闭socket的操作。  
  所以你的CDGSocket不具备拷贝构造函数也是很正常的。boost还专门有个noncopyable类,从该类派生的类都不具备拷贝构造和赋值操作!

4 楼dylgsy(一雨田)回复于 2006-07-22 01:02:04 得分 0

两位大虾,都说得不错,谢谢先。  
  to   甘草:  
  Socket是不应该具有拷贝构造语意的对象。  
  这句话有点不明,那是否说不可以使用这个方法返回?只能用第一种方法呢?  
   
  to   sinall:  
  我的析构函数里确实有关闭socket的操作,但是为什么不可以在这里关闭socket呢?socket不是被拷贝了吗?关闭了原来的socket,有什么关系呢?不太明白,请说清楚点。谢谢!!  
  下面是我的构造和析构函数的定义:  
  CDGSocket::CDGSocket()  
  {  
  m_hSocket   =   INVALID_SOCKET;  
  m_hSocket   =   socket(AF_INET,   SOCK_STREAM,   IPPROTO_TCP);  
  if(m_hSocket   ==   INVALID_SOCKET)  
  {  
  ThrowException("socket()   failed!");  
  }  
  }  
   
  CDGSocket::~CDGSocket()  
  {  
  if(m_hSocket   !=   INVALID_SOCKET)  
  Close();  
  }  
   
  //   这里的close函数如下:  
  void   CDGSocket::Close()  
  {  
  if(m_hSocket   !=   INVALID_SOCKET)  
  {  
  if(SOCKET_ERROR   ==   closesocket(m_hSocket))  
  ThrowException("CloseHandle(m_hSocket)   failed!");  
  m_hSocket   =   INVALID_SOCKET;  
  }  
  }  
   
  请再帮忙看看,十分感谢!!   
 

5 楼UPCC(杂食动物)回复于 2006-07-22 01:20:10 得分 40

我的析构函数里确实有关闭socket的操作,但是为什么不可以在这里关闭socket呢?socket不是被拷贝了吗?关闭了原来的socket,有什么关系呢?  
  --------------------------------------------------------------  
  其实我给你说个简单的,Socket是什么?Socket其实什么都不是,只是对一类技术操作的一个概念而已,真实的,他是一个象文件指针一类的东西。系统接受到一个连接请求就在内存打开一个新的文件,文件的指针也就是新的Socket了。这个文件存放了所有连接内容有关的一切东西,这一点你可以你查看Linux的源代码,或者别的技术文档。  
   
  你在你的类的析构函数里摧毁了Socket,那么文件被关闭,返回一个被关闭的文件的指针有作用吗?(备注:这一段是将事论事的,你程序具体什么问题,我不能确认)

6 楼dylgsy(一雨田)回复于 2006-07-22 01:32:37 得分 0

感谢UPCC,我对你说的话是这样理解的:  
  SOCKET是一个WINDOWS的分配的句柄,WINDOWS把这个当成一个标示,凡是要对SOCKET做IO操作,那就会调用这个句柄,然后这个句柄如果被关闭了,就相当于WINDOWS不再认这个句柄了,也就是对它进行的任何操作都会失效,这里的拷贝构造函数,也只是拷贝了它的数值而已,WINDOWS再也不认这个标示了,所以,我们不能对它做任何操作了。也就是甘草所说的,这是一类不具备拷贝构造函数语义的对象。对吗?  
  那按照这种结论,就只能使用第一种的方法去获取SOCKET了。  
  这是根据你们三位所说总结出来的,不知道理解对不对。请再指教,谢谢!

7 楼UPCC(杂食动物)回复于 2006-07-22 01:45:45 得分 0

对的。

8 楼UPCC(杂食动物)回复于 2006-07-22 01:47:38 得分 0

但对关技术细节的东西要分开来处理,毕竟实际的东西比理论的东西复杂

9 楼dylgsy(一雨田)回复于 2006-07-23 13:07:40 得分 0

自己再做了个试验:  
  SOCKET   s1   =   accept(...);  
  SOCKET   s2   =   s1;  
  closesocket(s1);  
   
  send(s2,   ...);  
  recv(s2,   ...);  
   
  发现s1被关闭之后,s2也不可用了,再证明了上述的结论是正确的,好,谢谢各位,结贴。

总结:

在现在回看起来,SOCKET只是一个数值(WINDOWS管理的一个数值),而对象是一个有一定内存空间的东西,例如方法、成员变量等都需要占用一定的内存空间。从这一点来看,SOCKET不能称之为一个对象,就像文件句柄、线程句柄等。所以针对这些类型的变量,我们一定要小心,它们是不具有拷贝构造的语义的。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值