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

TcpConnect,一个socket包装类

 对原始socket最重要的一个抽象:在一个TCP连接中,涉及到3类socket,专门负责接收连接的listenner,和接收到的accepter,以及主动进行连接的connecter。各种失效的情况...
  • hh_xj
  • hh_xj
  • 2010年01月12日 15:23
  • 1144

(第14讲)包装类的装箱问题以及Integer缓存问题

第十四讲 1、包装类的装箱问题分为自动装箱(Auto Boxing)和自动拆箱(Auto-unboxing)。 自动装箱(Auto Boxing):是指从基本数据类型转换到相应的包...
  • weiyastory
  • weiyastory
  • 2016年04月11日 19:27
  • 189

Java中的包装类及其注意事项

我们知道,在Java中,除了8
  • Bettarwang
  • Bettarwang
  • 2014年05月18日 22:17
  • 1341

包装类的自动装拆箱及Integer类的缓存区问题

jdk1.5后,出现了自动装箱和拆箱机制。即基本数据类型值可以赋值给基本数据类型的包装类,并可以和基本数据类型值直接运算。那么,这是如何实现的呢?看下面代码示例:/** * 基本数据类型的包装类,...
  • hello_java_noob_go
  • hello_java_noob_go
  • 2017年02月17日 14:50
  • 71

Java基础——包装类

Java是面向对象的编程语言,但它也包含了8种基本数据类型。这8种数据类型不支持面向对象的编程机制,基本数据类型的数据也不具备“对象”的特性:没有成员变量、方法可以被调用。这8种数据类型带来了一定的方...
  • OREO_GO
  • OREO_GO
  • 2016年07月29日 20:21
  • 362

【小细节】Integer缓存机制(包装类型的缓存机制)

前几天推送过一篇关于Integer比较大小的文章,具体可以查看《【细节】Integer细节比较》,有朋友提到说,Integer是有小数据缓存的机制的,那么第一个是否应该是true呢? 回归下第一...
  • losetowin
  • losetowin
  • 2015年11月21日 22:03
  • 1053

JAVA之包装类Byte详解

1.构造方法: ①Byte(byte value):以byte变量作为参数,创建Byte对象。 byte x = 23; Byte b = new Byte(x); ②Byte(String ...
  • m631521383
  • m631521383
  • 2013年08月08日 09:59
  • 2331

利用基本数据类型的包装类中的方法实现类型转换

几种常见的数据类型 int---------Integer    整型类型 long ------Long    长整型 float ---...
  • u014380256
  • u014380256
  • 2015年10月15日 11:04
  • 531

java中包装类的作用

一):它们的区别是:Integer是一个引用类型,而一个int是一个值类型 二):以List为例,大家都知道一个动态数组的add(Object o)方法只能接受一个引用类型,即一个对象,而怎样把一个数...
  • jierui001
  • jierui001
  • 2009年02月04日 13:25
  • 5222

java包装类的实例化秘密

包装类的实例化 所有的包装类都是不可变的 实例化方法: 构造方法 除了Character类,其他包 Integer(int x) Integer(String x) Number...
  • hephec
  • hephec
  • 2014年11月24日 16:01
  • 930
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:一个Socket包装类的问题
举报原因:
原因补充:

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