一个Socket包装类的问题

原创 2007年09月25日 11:37:00

在以前编写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不能称之为一个对象,就像文件句柄、线程句柄等。所以针对这些类型的变量,我们一定要小心,它们是不具有拷贝构造的语义的。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Java关于Integer包装类的一个坑

这也算是一道老题目了,但还是想拿出来说一说,毕竟自己在上家公司也掉在这个坑里。我这里先来段代码: ``` java public class IntegerTest { public static ...

C++沉思录读书笔记(23,24章)- 设计一个针对C接口的C++包装类

真理:使用C接口我们必须遵守它的一些隐藏约定,如果不遵守这些约定的话在程序运行时可能会出现奇怪的情况而导致失败,我们可以使用C++的数据抽象概念对C接口进行改进;对于C++而言,如果对某个类对象的所有...

关于Filter解决全网乱码问题的总结 --- 包装类 编解码 过滤器

中国的程序猿在编程过程中免不了要解决乱码问题,针对不同的提交方式有不同的解决方式。这两天刚好在学这个知识点,在此做一个简单的小结。首先要明白乱码出现的原因:乱码出现的根本原因是http协议不支持中文在...

Java包装类

  • 2016-09-19 00:38
  • 492KB
  • 下载

Java包装类对象比较中存在的问题

本文章以Integer包装类为例,讲解一些包装类比较过程中存在的问题。 首先看一段代码: Integer i1 = 10; Integer i2 = 10; ...

Java 包装类

我的MVC框架————GothaMVC(1)web容器和handler包装类

从年前到年后,前前后后断断续续写了差不多一个多月的框架终于大功告成啦~借鉴了springMVC的设计原理和相当多的设计模式。这一章我们先看一下在HTTP请求发来之前的准备工作,首先当然是MVC本身需要...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)