SOCKET编程的小细节和误区

1. UDP使用的误区, 一个socket, 存在两个线程, 同时调用sendto和recvfrom, 是可行的, (阻塞模式下已经通过测试), 同时存在多个线程调用sendto或recvfrom才是不可行的.  采用一个socket进行recvfrom, 把sockaddr拿出来, 用另外的一个socket进行sendto是可行的, 而且效率会比用一个socket要高.


2. TCP传送时使用结构体, 很经常就是采用统一的结构体进行发送数据, 甚至是采用继承的方式做, 这样的做法其实很浪费带宽的和栈空间的. 最好的办法是自定义内存流类,  并提供常用类型的写入和读取. 每种要传送的对象只要简单实现序列和散列的统一接口即可实现轻松管理.  而且也不必因为要以字节对齐, 字节顺序烦恼那么多.

    2013-1-22更新: 所谓的内存流对象, 是指由内部创建好内存块, 向外提供写入BYTE, WORD, DWORD, UINT64, ASCII string, Unicode string 等的序列化接口, 外部就不需要定义结构体来做什么了. 在发送端是按顺序写入WORD, DWORD, UINT64, 那么在接收端也采用流化对象把TCP接收到的流按链表连接起来, 在内部计算内存偏移, 并提供读取WORD, DWORD, UINT64等的接口, 按顺序读取出来即可. 对于字节对齐和字节顺序, 由这一个成对存在的序列和散列对象来保证, 其他任何地方都不需要额外注意了. 简单举个例子

class A {......};  // 应用层使用的对象

struct A{BYTE by, WORD w, DWORD dw}; // class A 进行序列化时要使用的结构体.

结构体要用于网络传送, 一定要加上按1BYTE对齐的方式, 以防止不同计算机的对齐方式不同, 但采用这种方式的情况下, 这个结构体在读取和写入的时候效率是有所损失的.

那么 class A 要转变成 struct A的时候, 还必须要注意字节顺序, by变量只有1B, 不用考虑, 但w变量有2B, 需要使用 htons 函数来变换, dw变量有4B, 需要使用htonl函数来变换. 当然, 变换成结构体形式的原因, 方便是在于send(&structA, sizeof(structA)...)就可以发送出去了, 但这样会导致定义非常多的结构体, 并且每个结构体的填写均要注意好htonX类函数的使用.

但假如预先弄好一对序列和散列的对象.

class CStreamWrite 和 class CStreamRead, 情况就不一样了, 这样就可以在这些stream对象里面实现紧密的序列化, 并且能够实现跨缓冲块的流读取形式. 采有这序列和散列对象时, class A 要执行序列化, 适用于 send 函数时候, 只需要

streamwrite.WriteBYTE(by)

streamwrtie.WrtieWORD(w)

streamwrtie.WriteDWORD(dw).

send的时候, 只需要 while (streamwrite.GetBuffer( &addr, &len )) send (addr, len...);

在服务器接收的过程当中, 可以简单的

while (0 < (len = recv(buffer, bufferlen ....)))

{

  streamRead.AddBuffer(buffer,  len);

  buffer = new char[bufferlen];

}

读取时就是按 BYTE, WORD, DWORD等形式读取出来即可.

更有一点最方便的, 就是可以在序列和散列对象里面组建好防止粘包的包头, 从而实现分包. 这样外部使用TCP发送的时候, 就可以完全脱离TCP层, 而是针对序列散列对象来处理数据即可. 在接收的时候, 还可以减少memcpy的开销, 因为内存是创建好让recv写进去的, 然后就直接交由CStreamRead对象来管理了,  这样比采用结构体不论适应性还是可变性都强.




3. 不要以为序列和散列是很麻烦的. C++里面也可以方便得像JAVA, C#那样的, 只要熟悉类成员的地址偏移, 直接把要序列化的一整个块拿出来, 并且可以根据类型来采用不同的序列化方式, 换句话说, 也就是几个序列化的通用函数就可以实现多种类型的序列和散列.


4. 采用UDP的话,  开一个UDP的SOCKET, 然后开启两个线程, 一个用于发送, 一个等待接收, 明明是阻塞模式, 但却发现接收线程老返回错误10054和10022. 这两个错误的原因如下:

10054 (WSAECONNRESET):  这个是微软说winsock的一个小BUG, 通过如下代码先设置SOCKET再阻塞等待就可以解决:

#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)

DWORD dwBytesReturned = 0;
BOOL bNewBehavior = FALSE;
DWORD status;
status = WSAIoctl(SendSocket, SIO_UDP_CONNRESET, &bNewBehavior, sizeof(bNewBehavior), NULL, 0, &dwBytesReturned, NULL, NULL);


10022 (WSAEINVAL): 这个是由于SOCKET还没有绑定端口, 所以才会出现的错误, 只要绑定端口就不会在等待数据时不阻塞了


5. 采用TCP的时候, 被防火墙, 360等Block的时候, 会导致出现一种情况: connect成功, 但send失败, 但防火墙开回来了, 这个socket是否依旧有效? 已经无效了, 再怎么用这个socket也send不出数据, recv不到数据, 必须要重新创建socket.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值