• ioctl socket getsockopt


    一 ioctl

    函数产生原因:

    虽然在文件操作结构体"struct file_operations"中有很多对应的设备操作函数,但是有些命令是实在找不到对应的操作函数。如CD-ROM的驱动,想要一个弹出光驱的操作,这种操作并不是所有的字符设备都需要的,所以文件操作结构体也不会有对应的函数操作。

    出于这样的原因,ioctl就有它的用处了——一些没办法归类的函数就统一放在ioctl这个函数操作中,通过指定的命令来实现对应的操作。所以,ioctl函数里面都实现了多个的对硬件的操作,通过应用层传入的命令来调用相应的操作。

    函数作用:

    ioctl函数操纵底层设备参数

     

    函数参数:

    ioctl(int fd,int command, (char*)argstruct)

    fd 文件描述符,实际上是由socket()系统调用返回的。

    command是设备相关的请求的代码

    第三个参数是一个无类型指针的内存。

    函数例子:

    二 select

    Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。

    函数原型

    Select的函数格式(我所说的是Unix系统下的伯克利socket编程,和windows下的有区别,一会儿说明):

    int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

    先说明两个结构体:

    第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set *),将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。一会儿举例说明。

    第二,struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。

    具体解释select的参数:

    int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。

    fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。

    fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。

    fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。

    struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

    返回值:

    负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件

    简单例子:

    #include <stdio.h>

           #include <sys/time.h>

           #include <sys/types.h>

           #include <unistd.h>

           int

           main(void) {

               fd_set rfds;

               struct timeval tv;

               int retval;

               /* Watch stdin (fd 0) to see when it has input. */

               FD_ZERO(&rfds);

               FD_SET(0, &rfds);

               /* Wait up to five seconds. */

               tv.tv_sec = 5;

               tv.tv_usec = 0;

               retval = select(1, &rfds, NULL, NULL, &tv);

               /* Don’t rely on the value of tv now! */

               if (retval)

                   printf("Data is available now. ");

                   /* FD_ISSET(0, &rfds) will be true. */

               else

                   printf("No data within five seconds. ");

               return 0;

           }

    三 setsockopt

    函数原型:

    int PASCAL FAR setsockopt( SOCKET s, int level, int optname, const char FAR* optval, int optlen);

    设置套接口的选项。

    s:标识一个套接口的描述字。

    level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。

    optname:需设置的选项。

    optval:指针,指向存放选项值的缓冲区。

    optlen:optval缓冲区的长度。

    setsockopt()的使用是十分复杂的,其功能是很丰富的。setsockopt()函数用于任意类型、任意状态套接口的设置选项值。有两种套接口的选项:一种是布尔型选项,允许或禁止一种特性; 另一种是整形或结构选项。允许一个布尔型选项,则将optval指向非零整形数; 禁止一个选项optval指向一个等于零的整形数。 对于布尔型选项,optlen应等于sizeof(int) ; 对其他选项,optval指向包含所需选项的整形数或结构,而optlen则为整形数或结构的长度。

    返回值: 若无错误发生,setsockopt()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。 错误代码:

    WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。

    WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。

    WSAEFAULT:optval不是进程地址空间中的一个有效部分。

    WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。

    WSAEINVAL:level值非法,或optval中的信息非法。

    WSAENETRESET:当SO_KEEPALIVE设置后连接超时。

    WSAENOPROTOOPT:未知或不支持选项。其中,SOCK_STREAM类型的套接口不支持SO_BROADCAST选项,SOCK_DGRAM类型的套接口不支持SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE选项。

    WSAENOTCONN:当设置SO_KEEPALIVE后连接被复位。

    WSAENOTSOCK:描述字不是一个套接口。

    具体使用如下:

    1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:

    BOOL bReuseaddr=TRUE;

    setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));

    2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历 TIME_WAIT的过程:

    BOOL bDontLinger = FALSE;

    setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));

    3.在send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限:

    int nNetTimeout=1000;//1秒

    //发送时限

    setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int));

    //接收时限 setsockopt(socket,SOL_S0CKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int));

    4.在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节 (异步);系统默认的状态发送和接收一次为8688字节(约为8.5K);

    在实际的过程中发送数据和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发:

    // 接收缓冲区

    int nRecvBuf=32*1024;

    //设置为32K

    setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));

    //发送缓冲区 int nSendBuf=32*1024;//设置为32K

    setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));

    5. 如果在发送数据的时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响程序的性能:

    int nZero=0;

    setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));

    6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区):

    int nZero=0;

    setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int));

    7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:

    BOOL bBroadcast=TRUE;

    setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL));

    8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的作用,在阻塞的函数调用中作用不大)

    BOOL bConditionalAccept=TRUE;

    setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL));

    9.如果在发送数据的过程中(send()没有完成,还有数据没发送)而调用了closesocket(),以前我们一般采取的措施是"从容关闭"shutdown(s,SD_BOTH),但是数据是肯定丢失了,如何设置让程序满足具体应用的要求(即让没发完的数据发送出去后在关闭socket)?

    struct linger

    {

    u_short l_onoff;

    u_short l_linger;

    };

    linger m_sLinger;

    m_sLinger.l_onoff=1;//(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留) 如果m_sLinger.l_onoff=0;则功能和2.)作用相同;

    m_sLinger.l_linger=5; //(容许逗留的时间为5秒)

    setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));

     四 getsockopt

    int PASCAL FAR getsockopt( SOCKET s, int level, int optname, char FAR* optval, int FAR* optlen);

    s:一个标识套接口的描述字。

    level:选项定义的层次。支持的层次仅有SOL_SOCKET(套接字水平)和IPPROTO_TCP。

    optname:需获取的套接口选项。

    optval:指针,指向存放所获得选项值的缓冲区。

    optlen:指针,指向optval缓冲区的长度值。

    返回值: 若无错误发生,getsockopt()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。

    错误代码:

    WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。

    WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。

    WSAEFAULT:optlen参数非法。

    WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。

    WSAENOPROTOOPT:未知或不支持选项。其中,SOCK_STREAM类型的套接口不支持SO_BROADCAST选项,SOCK_DGRAM类型的套接口不支持SO_ACCEPTCONN、SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE选项。 WSAENOTSOCK:描述字不是一个套接口。

  • 相关阅读:
    msgs no .h file
    我们为之奋斗过的C#之---简单的库存管理系统
    我们为之奋斗过的C#-----C#的一个简单理解
    套接字之sendto系统调用
    套接字之msghdr结构
    套接字之select系统调用
    套接字之close系统调用
    TCP之listen&backlog
    套接字之相关系统调用的调用流程
    套接字之shutdown系统调用
  • 原文地址:https://www.cnblogs.com/zhouhbing/p/4304913.html
Copyright © 2020-2023  润新知