socket编程注意点

搞了一星期的socket通信,头都大了,还好终于给我调通了!哈哈~总结下Socket编程时需要注意的问题,或者说是编写socket程序时,需要考虑的问题:

1.协议的选择,是TCP还是UDP

众所周知,TCP提供可靠连接,UDP提供不可靠传输。对于“不重要”的数据通信建议采用UDP,简单没有那么多的报文确认,而且即便丢几帧也是可以的。对于“重要”的数据通信那就要用TCP,能保证可靠的通信

2.套接字阻塞/非阻塞模式

阻塞模式,就是recv /recvfrom只有等到有数据来了才返回,否则就一直在那死等。

非阻塞模式,意思很明显,就是创建这样的套接字之后,winsock API调用会立即返回,通常,我们需要重复调用同一个函数,直到成功返回,因此,对于非阻塞模式下的socket函数调用,我们要时刻检查返回值的情况,以确保革命成功

3.套接字I/O模型选择,共有5种模型:select模型,异步窗口模型,异步事件模型,overlapped模型,以及完成端口模型。

这里,有必要先帮大家理清下套接字模式和套接字I/O模型的区别。套接字模式是用于决定在随一个套接字调用时,那些winsock函数的行为。而套接字模型则定义了一个应用程序如何对套接字上进行的I/O进行管理及处理。(windows网络编程技术中的解释)

大概的意思就是,套接字模式好比两种不同性格的人,一种是决定了一直要死等天上掉钱,否则饭也不吃觉也不睡;或者,我只是来打酱油的,没钱就立马走人!而套接字I/O模型好比是个热心肠,他说,大哥,你别着急,坐下来歇会,我先帮你盯着,一有钱下来我就告诉你!

select模型

核心是围绕select函数做文章。

int select{

       int nfds,//忽略

       fd_set FAR * readfds,//可读性检查

       fd_set FAR * writefds,//可写性

       fd_set FAR * exceptfds,//例外数据

       const struct timeval FAR *timeout //等待I/O时间,超过则返回

}

FD_SET是一个集合,使用时,假设我们想接收socket1的数据,但又不想死等,这是可以调用 FD_SET(SOCKET s)socket1添加进readfds集合中,这样,我们先调用ret=select(…),在timeout时间内,若有数据可接收,则立即返回,下面我们就可以调用recv来接收数据,若ret表示超时,则当前没有数据可读,还有可能返回socket_error。总之,select相当于是个侦察兵,他可以让心急的我们(阻塞模式的socket)在恰当的时候出击,以保证万无一失~

注:关于FD_SET集合的操作(FD_ZERO,FD_ISSET,FD_CLR)《indows网络编程技术》有详细说明。

WSAAsyncSelect模型

异步I/O模型。利用该模型,应用程序可以在一个套接字上,接收以Windows消息为基础的网络事件通知。创建时,要调用WSAAsyncSelect函数,看到函数参数,就明白了,这就相当于给指定窗口定义了一个自定义消息,注册了触发消息的事件。这个消息是由socket发出的,窗口接收到消息后,根据消息类型,判断到底发生了什么socket事件。

注意的是,响应函数是有格式要求的:

LRESULT CALLBACK WindowProc(

       HWND hWnd,

       UINT uMsg,

       WPARAM wParam,

       LPARAM lParam);

其中,wParam指定了“肇事”的是哪个socketlParam包含两方面信息,低位字指定发生了什么网络事件,高位字包含了可能出现的任何错误代码。使用时,应该先判断错误代码,若没有错误,再分析事件类型进行处理。

WSAEventSelect模型

异步事件通知模型,要求我们为套接字创建一个事件对象(WSACreateEvent()),这个事件对象中注册了你感兴趣的网络事件(FD_ACCEPT,FD_READ…),然后,将该事件对象与套接字绑定(WSAEventSelect),这样,如果该套接字上有收到数据,那么我们就会收到FD_READ信号,提示我们接收数据。我们可以调用WSAWaitForMultipleObjectWSAEnumNetWorkEvents函数来确定是哪个socket发生的什么网络事件。

重叠模型

完成端口模型

注:④⑤这两种模型没用过,《Windows网络编程技术》中有详细讲解,关于其使用的总结有经验的朋友可以介绍下~

 

4.对于TCP流式传输和UDP的包传输的理解。这个直接影响到编程思想,即如何接收数据的问题。即你是按帧接收还是按数据流的思想接收。

5.setsockopt/getsockopt设置socket的系统选项

setsocketopt中你可以配置socket的系统参数,比如系统分配的收发缓冲区等,具体功能我在另一篇文章中讲到,大家可以看看,在网上找的~

6.系统预定义的socket收发缓冲区大小是否要改变

当我们调用send/sendto发送数据时,返回成功,这时,数据只是被暂存在了系统预分配的发送缓冲区中,实际发送成功是要等TCPack报文才算数。增加这个缓冲,一定程度上能降低我们的重传次数,但只是缓兵之计,并非良药~

7. FD_ACCEPT,FD_CONNECT,FD_READ,FD_WRITE,FD_CLOSE信号,何时触发

这里重点讲何时发出FD_WRITE信号:

1)使用connect/WSAConnect,一个套接字首次建立了连接

2)使用accept/WSAAccept,套接字被接受以后

3)若send/WSASend/sendto/WSASendTo操作失败,并且返回WSAEWOULDBLOCK错误(该错误表明发送缓冲区溢出),而且缓冲区又变得可用了

总之,FD_WRITE信号貌似不怎么好用,有经验的可以分享下经验。。。

8.Send/recvsendto/recvfrom返回值的含义。对返回值的处理是保证正确的通信的基础

9.UDP丢包后的重传机制

10TCP粘包现象处理

一般是在接收端,设立一个大缓冲区,收到数据时,先统统放到该缓冲区中,再进行数据的处理。

11.信号量的使用,包括Cevent,WSAEvent,Cmutex

使用事件,能帮助我们更好的控制通信,具体用法因人而异,Cmutex用于对某个缓冲区操作时进行锁定,以防止多方同时操作一个对象,但是,在使用时,我们应该尽量避免使用互斥对象,因为系统在处理时是蛮复杂的。。。

12.监听线程如何高效的循环等待网络事件,不要用while(1),用WaitForSingleObject/WaitForMultipleObject等,这些是高效的等待函数,当没有事件时,线程是挂起的,处于阻塞状态,不占用CPU时间。

13Socket发送数据较简单,如何接收数据是需要注意的,特别是循环发送大量数据时,接收程序更应该考虑

这里,有两种策略:

1)每次发送之间,增加一个延时,以协调通信双方的速度,但这不能根本解决问题

2)如上面所说,收到的数据都先放到一个大缓冲区中,然后再按帧取数据,这里,如何按帧取数据,可以在发送帧的时候,在帧头写明这一帧的长度,这样就好了~

最后,以上的总结都是本人自己的想法,有不妥或需要补充的地方希望各位多多指教!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值