淺析:setsockopt()改善socket網路程式的健壯性

來源:互聯網
上載者:User

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  :    8751webmaster@126.com              
//    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. 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.