getsocket /setsocket/fcntl

来源:互联网 发布:网络机构图用什么软件 编辑:程序博客网 时间:2024/05/16 18:23

函数原型为:
 
#include <netinet/socket.h>
 
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
Return: 0 if OK, –1 on erro
这个函数用于获得socket的各种特性,即socket options. 结果放在函数的后两个参数中,这两个参数是value-result。
 
sockfd 是所要查看的socket的file descriptor
 
level 和 optname 在Figure 7.1 中(Unix Network Programming, P.193)
 
optval,getsockopt() 函数把所得到的socket option的值放到这个参数之中。它的数据类型要和Figure 7.1中的Datatype一致。
 
optlen 作为参数是表示optval 的大小。作为结果是表示返回的 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:描述字不是一个套接口。
 一、 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));
 
#include "unp.h"
#include <netinet/tcp.h>

int main(int argc, char **argv)
{
 int fd, val;
 socklen_t len;
 char strres[128];
 
 len = sizeof(val);
 fd = Socket(AF_INET, SOCK_STREAM, 0);
 if(getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, &len) == -1)
 {
  err_ret("getsockopt error");
 }
 else
 {
  if(len != sizeof(int))
   snprintf(strres, sizeof(strres), "sizeof (%d) not sizeof(int)", len);
  else
   snprintf(strres, sizeof(strres), "%d", val);
  printf("default = %s\n", strres);
 }
 close(fd);
 exit(0);
}
 
这里是查看receive buffer的大小。

SO_BROADCAST 开启或禁止进程发送广播消息的能力

SO_KEEPALIVE  给一个TCP设置保持存活(keep alive)选项后,如果2小时内在该套接口的一方向上都没有数据交换,
TCP就会自动给对端发送一个保持存活探测分节,这是一个对端必须响应的TCP分节

2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历 TIME_WAIT的过程:
BOOL bDontLinger = FALSE;
setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));

如果在发送数据的过程中(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));

在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节 (异步);系统默认的状态发送和接收一次为8688字节(约为8.5K),典型的MSS值为512或1460;
在实际的过程中发送数据和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发:套接口中接收缓冲区可用大小限定了TCP
通告对端的窗口大小 ,TCP接收缓冲区不可能溢出,因为不允许对端发出超过本段所通告窗口大小的数据。如果对端发出了超过该窗口大小的数据,本端
TCP将丢弃它们。UDP是没用流量控制,所以发送端可以很容易的淹没较慢的接收端。
// 接收缓冲区
int nRecvBuf=32*1024;
//设置为32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int)); //对于客户接收数据,必须在调用connect之前设置。
//发送缓冲区 int nSendBuf=32*1024;//设置为32K ,大小至少是相应连接的MSS值的4倍
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));//对于服务器,必须在listen之前设置
在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));
struct timeval ltimeout = {30, 0};注意 参数是指向timeval结构的指针
setsockopt(lisocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&ltimeout, sizeof(ltimeout));

8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被呼叫
(本函数设置只有在非阻塞的过程中有显著的作用,在阻塞的函数调用中作用不大)
BOOL bConditionalAccept=TRUE;
setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL));

SO_REUSEADDR 设置套接字选项为SO_REUSEADDR,socket可重用,经常在socket通信时进行设置
 1、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启
动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。

//设置套接字选项为SO_REUSEADDR,即允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将该端口用作它们的本地端口的连接仍存在。
例如:
1,启动一个监听服务器。
2,连接请求到达,派生一个子进程来处理这个客户。
3,监听服务器终止,但子进程继续为现有连接上的客户提供服务。
4,重启监听服务器,它试图捆绑一个现有连接,(即子进程正在处理的连接),如果不设置此选项,那么在bind调用时将会失败。
这将允许在一个端口上启动多个服务器的实例。IP别名技术:用来托管HTTP站点。例如 有IP别名,但众所周知端口仅有一个,同样能够绑定到IP别名上。
注意:对于TCP,我们不能启动捆绑相同IP和相同端口号的多个服务器,即启动之后再次启动,即使设置了此套接字选项。
 
int opt=1;
 
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
 
:编写 TCP/SOCK_STREAM 服务程序时,SO_REUSEADDR到底什么意思?
 
A: 这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而TCP状态位于其他状态,
重用端口时依旧得到一个错误信息,指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧使用同一端口,
此时 SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不可能。
 
一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端口。SO_REUSEADDR 仅仅表示可以重用本地本地地址、
本地端口,整个相关五元组还是唯一确定的。所以,重启后的服务程序有可能收到非期望数据。必须慎重使用 SO_REUSEADDR 选项

SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。


fcntl :_GETFL    取得fd的文件状态标志,如同下面的描述一样(arg被忽略),在说明open函数时,已说明
了文件状态标志。不幸的是,三个存取方式标志 (O_RDONLY , O_WRONLY , 以及O_RDWR)并不各占1位。(这三种标志的值各是0 , 1和2,
由于历史原因,这三种值互斥 — 一个文件只能有这三种值之一。) 因此首先必须用屏蔽字O_ACCMODE相与取得存取方式位,然后将结果与这三种值相比较。      
F_SETFL    设置给arg描述符状态标志,可以更改的几个标志是:O_APPEND,O_NONBLOCK,O_SYNC 和 O_ASYNC。而fcntl的文件状态标志总共有7个:
O_RDONLY , O_WRONLY , O_RDWR , O_APPEND , O_NONBLOCK , O_SYNC和O_ASYNC
可更改的几个标志如下面的描述:
    O_NONBLOCK   非阻塞I/O,如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,则read或write调用将返回-1和EAGAIN错误
    O_APPEND     强制每次写(write)操作都添加在文件大的末尾,相当于open(2)的O_APPEND标志
    O_DIRECT     最小化或去掉reading和writing的缓存影响。系统将企图避免缓存你的读或写的数据。如果不能够避免缓存,那么它将最小化已经被缓存了的数据造成的影响。如果这个标志用的不够好,将大大的降低性能
    O_ASYNC      当I/O可用的时候,内核产生一个SIGIO,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候。
F_GETOWN   取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,SIGIO是套接口被设置成信号驱动I/O时产生的,SIGURG是在新的带外数据到达套接口时产生的。
注意设置的方法是先取得当前标志,与新标志逻辑或后再设置标志。

原创粉丝点击