套接口在建立以后,可以通过设置套接口的属性对套接口的行为和操作进行控制,这就要使用套接口选项设置函数setsockopt()。也可以在套接口上进行操作前首先查看套接口属性的设置情况,这就要使用套接口选项获取函数getsockopt():
int setsockopt(
__in SOCKET s, //指定一个有效的套接口
__in int level, //套接口选项定义的级别,常用的有SOL_SOCKET、IPPROTO_IP和IPPROTO_TCP
__in int optname, //需设置的套接口选项,选项的名称是在Winsock头文件中定义的常量值
__in const char *optval, //指向存放选项值缓冲区的指针
__in int optlen //指向optval缓冲区长度的指针
);
int getsockopt(
__in SOCKET s, //指定一个有效的套接口
__in int level, //套接口选项定义的级别,常用的有SOL_SOCKET、IPPROTO_IP和IPPROTO_TCP
__in int optname, //需获取的套接口选项,选项的名称是在Winsock头文件中定义的常量值
__out char *optval, //指向存放选项值缓冲区的指针
__inout int *optlen //指向optval缓冲区长度的指针
);
若函数调用成功,则setsockopt()和getsockopt()函数都返回0。若调用时发生错误,则返回SOCKET_ERROR错误信息,应用程序通过WSAGetLastError()函数获取对错误信息的进一步说明
1)SOL_SOCKET选项级别:
SOL_SOCKET选项级别主要针对传输层协议(TCP或UDP)。在SOL_SOCKET选项级别下,套接口的选项有两种类型:一种是值为布尔型(BOOL)的选项,这种选项可以允许或禁止一种特性;另一种是值为整型(int)或结构型(Struct Linger)的选项,这种选项可以用来设置系统工作时的某些参数。
对于布尔型选项,oprlen应等于sizeof(int);对于非布尔型的其他选项,optval应该指向包含所需选项的整型量或结构量,而optlen则为整型量或结构量的长度。
还要注意,套接口的有些属性值即可以设置(setsockopt),也可以获取(getsockopt),但有些套接口属性只能获取或只能设置。
SOL_SOCKET选项级别下的各选项:
2)IPPROTO_IP选项级别:
IPPROTO_IP级别的套接口选项是针对网络层协议的,即IPv4协议,这些选项字段的声明大都放在Winsock.h和WinSock2.h这两个头文件中。该选项级别包括的主要选项如下表:
3)IPPROTO_TCP选项级别:
该选项是针对TCP协议的,在Winsock中仅有一个IPPROTO_TCP级别的选项,即TCP_NODELAY选项,该选项用来打开或关闭Nagle算法。如果该选项为TRUE,则在对应的套接口上禁止使用Nagle算法。在系统默认情况下,Nagle算法是打开的,该选项只适用于流式套接口(SOCK_STREAM),其地址族是AF_INET。
Nagle算法将未确认的数据存入缓冲区,直到蓄足一个包后一起发送,这样做的好处是可以减少主机发送的零碎小数据包的数目,以减少网络通信的开销,提供系统的吞吐量。但对于某些应用来说,这种算法将降低系统性能,例如交互性较强的Telnet应用程序,用户可通过它登录另一台远程机器,然后向其传送命令。通常,用户每秒只会进行少量的键击,若使用Nagle算法,便会造成响应的迟钝,甚至造成对方主机不予应答的错觉。此时,就应该使用TCP_NODELAY选项将此算法关闭。应用程序编写者只有在确切了解它的效果并在确实需要的情况下,才可设置TCP_NODELAY选项,因为一般的应用中如果设置该选项,则对网络性能有明显的负面影响。
下面的代码演示的是setsockopt函数的使用:
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
//---------------------------------------
// Declare variables
WSADATA wsaData;
SOCKET ListenSocket;
sockaddr_in service;
//---------------------------------------
// Initialize Winsock
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(iResult != NO_ERROR)
printf("Error at WSAStartup/n");
//---------------------------------------
// Create a listening socket
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(ListenSocket == INVALID_SOCKET)
{
printf("Error at socket()/n");
WSACleanup();
return 1;
}
//---------------------------------------
// Bind the socket to the local IP address
// and port 27015
hostent* thisHost;
char* ip;
u_short port;
port = 27015;
thisHost = gethostbyname("");
ip = inet_ntoa(*(struct in_addr*)*thisHost->h_addr_list);
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(ip);
service.sin_port = htons(port);
if(bind(ListenSocket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR)
{
printf("bind failed/n");
closesocket(ListenSocket);
WSACleanup();
return 1;
}
//---------------------------------------
// Initialize variables and call setsockopt.
// The SO_KEEPALIVE parameter is a socket option
// that makes the socket send keepalive messages
// on the session. The SO_KEEPALIVE socket option
// requires a boolean value to be passed to the
// setsockopt function. If TRUE, the socket is
// configured to send keepalive messages, if FALSE
// the socket configured to NOT send keepalive messages.
// This section of code tests the setsockopt function
// by checking the status of SO_KEEPALIVE on the socket
// using the getsockopt function.
BOOL bOptVal = TRUE;
int bOptLen = sizeof(BOOL);
int iOptVal;
int iOptLen = sizeof(int);
if(getsockopt(ListenSocket, SOL_SOCKET, SO_KEEPALIVE,
(char*)&iOptVal, &iOptLen) != SOCKET_ERROR)
{
printf("SO_KEEPALIVE value: %ld/n", iOptVal);
}
if(setsockopt(ListenSocket, SOL_SOCKET, SO_KEEPALIVE,
(char*)&bOptVal, bOptLen) != SOCKET_ERROR)
{
printf("Set SO_KEEPALIVE: ON/n");
}
if(getsockopt(ListenSocket, SOL_SOCKET, SO_KEEPALIVE,
(char*)&iOptVal, &iOptLen) != SOCKET_ERROR)
{
printf("SO_KEEPALIVE value: %ld/n", iOptVal);
}
WSACleanup();
system("pause");
return 0;
}
=======================================================
下面代码演示了getsockopt函数的使用:
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
void main() {
//---------------------------------------
// Declare variables
WSADATA wsaData;
SOCKET ListenSocket;
sockaddr_in service;
//---------------------------------------
// Initialize Winsock
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
if( iResult != NO_ERROR )
printf("Error at WSAStartup/n");
//---------------------------------------
// Create a listening socket
ListenSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket()/n");
WSACleanup();
return;
}
//---------------------------------------
// Bind the socket to the local IP address
// and port 27015
hostent* thisHost;
char* ip;
u_short port;
port = 27015;
thisHost = gethostbyname("");
ip = inet_ntoa (*(struct in_addr *)*thisHost->h_addr_list);
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(ip);
service.sin_port = htons(port);
if ( bind( ListenSocket,(SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR ) {
printf("bind failed/n");
closesocket(ListenSocket);
return;
}
//---------------------------------------
// Initialize variables and call getsockopt.
// The SO_ACCEPTCONN parameter is a socket option
// that tells the function to check whether the
// socket has been put in listening mode or not.
// The various socket options return different
// information about the socket. This call should
// return 0 to the optVal parameter, since the socket
// is not in listening mode.
int optVal;
int optLen = sizeof(int);
if (getsockopt(ListenSocket,
SOL_SOCKET,
SO_ACCEPTCONN,
(char*)&optVal,
&optLen) != SOCKET_ERROR)
printf("SockOpt Value: %ld/n", optVal);
//---------------------------------------
// Put the listening socket in listening mode.
if (listen( ListenSocket, 100 ) == SOCKET_ERROR) {
printf("error listening/n");
}
//---------------------------------------
// Call getsockopt again to verify that
// the socket is in listening mode.
if (getsockopt(ListenSocket,
SOL_SOCKET,
SO_ACCEPTCONN,
(char*)&optVal,
&optLen) != SOCKET_ERROR)
printf("SockOpt Value: %ld/n", optVal);
WSACleanup();
return;
}