淺析:setsockopt()改善socket網絡程序的健壯性

1. 如果在已經處於 ESTABLISHED狀態下的socket(一般由端口號和標誌符區分)調用
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));
Note:1.在設置了逗留延時,用於一個非阻塞的socket是作用不大的,最好不用;2.如果想要程序不經歷SO_LINGER需要設置SO_DONTLINGER,或者設置l_onoff=0;

10.還一個用的比較少的是在SDI或者是Dialog的程序中,可以記錄socket的調試信息:
(前不久做過這個函數的測試,調式信息可以保存,包括socket建立時候的參數,採用的
具體協議,以及出錯的代碼都可以記錄下來)
BOOL bDebug=TRUE;
setsockopt(s,SOL_SOCKET,SO_DEBUG,(const char*)&bDebug,sizeof(BOOL));

11.附加:往往通過setsockopt()設置了緩衝區大小,但還不能滿足數據的傳輸需求,
我的習慣是自己寫個處理網絡緩衝的類,動態分配內存; 一般的習慣是自己寫個處理網絡緩衝的類,動態分配內存;下面我將這個類寫出,希望對大家有所幫助:  
 
//仿照String    改寫而成  
//==============================================================================  
//  二進制數據,主要用於收發網絡緩衝區的數據  
//  CNetIOBuffer  以  MFC  類  CString  的源代碼作爲藍本改寫而成,用法與  CString  類似,  
//  但是  CNetIOBuffer  中存放的是純粹的二進制數據,'\0'  並不作爲它的結束標誌。  
//  其數據長度可以通過  GetLength()  獲得,緩衝區地址可以通過運算符  LPBYTE  獲得。  
 
 
//==============================================================================  
//    Copyright  (c)  All-Vision  Corporation.  All  rights  reserved.  
//    Module:    NetObject  
//    File:        SimpleIOBuffer.h  
//    Author:    gdy119  
//    Email  :    [email][email protected][/email]              
//    Date:      2004.11.26  
//==============================================================================  
//  NetIOBuffer.h  
#ifndef  _NETIOBUFFER_H  
#define  _NETIOBUFFER_H  
//=============================================================================  
#define    MAX_BUFFER_LENGTH    1024*1024  
//=============================================================================  
//主要用來處理網絡緩衝的數據  
class    CNetIOBuffer      
{  
protected:  
           LPBYTE                            m_pbinData;  
           int                                  m_nLength;  
           int                                  m_nTotalLength;  
           CRITICAL_SECTION            m_cs;  
       void    Initvalibers();  
public:  
           CNetIOBuffer();  
           CNetIOBuffer(const  LPBYTE  lbbyte,  int  nLength);  
           CNetIOBuffer(const  CNetIOBuffer&binarySrc);  
           virtual  ~CNetIOBuffer();  
//=============================================================================              
           BOOL            CopyData(const  LPBYTE  lbbyte,  int  nLe  
---------------------------------------------------------------  
 
其實我覺得第5條很應該值得注意  
int  nZero=0;  
setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char  *)&nZero,sizeof(nZero));  
 
記得以前有些朋友討論過,socket雖然send成功了,但是其實只是發送到數據緩衝區裏面了,而並沒有真正的在物理設備上發送出去;而通過這條語句,將發送緩衝區設置爲0,即屏蔽掉髮送緩衝以後,一旦send返回(當然是就阻塞套結字來說),就可以肯定數據已經在發送的途中了^_^,但是這樣做也許會影響系統的性能  
 
---------------------------------------------------------------  
 
setoptsock()這個函數  設置成端口複用的時候,很容易對一些沒有進行單獨bind模式的程序造成危害。  
比如old的  ping  icmp  door,簡單的sniffer後,收到包,然後設置setoptsock  bind  web服務,然後建立個cmd進程  bind再80端口。
 
======================================
Example Code


The following example demonstrates the setsockopt function.



#include <stdio.h>
#include "winsock2.h"

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 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();
return;

}




Notes for IrDA Sockets

Keep in mind the following:


The Af_irda.h header file must be explicitly included.
IrDA provides the following settable socket option:
Value Type Meaning
IRLMP_IAS_SET *IAS_SET Sets IAS attributes


The IRLMP_IAS_SET socket option enables the application to set a single attribute of a single class in the local IAS. The application specifies the class to set, the attribute, and attribute type. The application is expected to allocate a buffer of the necessary size for the passed parameters.

IrDA provides an IAS Database that stores IrDA-based information. Limited access to the IAS Database is available through the windows Sockets 2 interface, but such access is not normally used by applications, and exists primarily to support connections to non-windows devices that are not compliant with the windows Sockets 2 IrDA conventions.

The following structure, IAS_SET, is used with the IRLMP_IAS_SET setsockopt option to manage the local IAS Database:


typedef struct _IAS_SET {
char irdaClassName[IAS_MAX_CLASSNAME];
char irdaAttribName[IAS_MAX_ATTRIBNAME];
u_long irdaAttribType;
union
{
LONG irdaAttribInt;
struct
{
u_short Len;
u_char OctetSeq[IAS_MAX_OCTET_STRING];
} irdaAttribOctetSeq;
struct
{
u_char Len;
u_char CharSet;
u_char UsrStr[IAS_MAX_USER_STRING];
} irdaAttribUsrStr;
} irdaAttribute;
} IAS_SET, *PIAS_SET, FAR *LPIAS_SET;

The following structure, IAS_QUERY, is used with the IRLMP_IAS_QUERY setsockopt option to query a peer's IAS Database:


typedef struct _windows_IAS_QUERY {
u_char irdaDeviceID[4];
char irdaClassName[IAS_MAX_CLASSNAME];
char irdaAttribName[IAS_MAX_ATTRIBNAME];
u_long irdaAttribType;
union
{
LONG irdaAttribInt;
struct
{
u_long Len;
u_char OctetSeq[IAS_MAX_OCTET_STRING];
} irdaAttribOctetSeq;
struct
{
u_long Len;
u_long CharSet;
u_char UsrStr[IAS_MAX_USER_STRING];
} irdaAttribUsrStr;
} irdaAttribute;
} IAS_QUERY, *PIAS_QUERY, FAR *LPIAS_QUERY;

Many SO_ level socket options are not meaningful to IrDA. Only SO_LINGER is specifically supported.

Note setsockopt must be called before bind on windows NT 4.0, windows 95, and windows 98 platforms.


Requirements
Client Requires windows XP, windows 2000 Professional, windows NT Workstation, windows Me, windows 98, or windows 95.
Server Requires windows Server 2003, windows 2000 Server, or windows NT Server.
Header Declared in Winsock2.h.

Library Link to Ws2_32.lib.

DLL Requires Ws2_32.dll. 


Trackback: [url]http://tb.blog.csdn.net/TrackBack.aspx?PostId=1745293[/url]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章