今天看redis代碼時,發現了如下代碼,設置阻塞socket的讀寫超時時間,仔細一看就是簡單的設置了一下socket的屬性,索性把socket一些屬性總結一下。
/* Set read/write timeout on a blocking socket. */ int redisSetTimeout(redisContext *c, const struct timeval tv)
1 讀超時
The timeout, in milliseconds, for blocking receive calls. The default for this option is zero, which indicates that a receive operation will not time out. If a blocking receive call times out, the connection is in an indeterminate state and should be closed. 注意,linux和windows的參數略不同。
bool SetRecvTimeOut(uint32 millisecond) { #ifdef WIN32 DWORD time = millisecond; if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&time, sizeof(DWORD)) == -1) { return false; } #else struct timeval tv; tv.tv_sec = millisecond / 1000; tv.tv_usec = (millisecond % 1000) * 1000; if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) == -1) { return false; } #endif return true; }
2 寫超時
The timeout, in milliseconds, for blocking send calls. The default for this option is zero, which indicates that a send operation will not time out. If a blocking send call times out, the connection is in an indeterminate state and should be closed.
注意,linux和windows的參數略不同。
bool SetSendTimeOut(uint32 millisecond) { #ifdef WIN32 DWORD time = millisecond; if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&time, sizeof(DWORD)) == -1) { return false; } #else struct timeval tv; tv.tv_sec = millisecond / 1000; tv.tv_usec = (millisecond % 1000) * 1000; if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)) == -1) { return false; } #endif return true; }
3 地址重用
Allows a socket to bind to an address and port already in use. The SO_EXCLUSIVEADDRUSE option can prevent this.
一個端口釋放后會等待兩分鍾之后才能再被使用,SO_REUSEADDR是讓端口釋放后立即就可以被再次使用。
p2p打洞時也需要設置這個屬性。
公司的網絡庫貌似都會設置這個屬性呢。
bool SetReUseAddr(int v) { int ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v)); return 0 == ret; }
4 keepalive
設置心跳包,還可以指定心跳包頻率,不過建議還是在邏輯層設計心跳協議,來檢查連接存活。
int val = 1; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val))
5 TCP_NODELAY
TCP_NODELAY和TCP_CORK基本上控制了包的“Nagle化”,這里我們主要講TCP_NODELAY.Nagle化在這里的含義是采用Nagle算法把較小的包組裝為更大的幀。JohnNagle是Nagle算法的發明人,后者就是用他的名字來命名的,他在1984年首次用這種方法來嘗試解決福特汽車公司的網絡擁塞問題(欲了解詳情請參看IETF RFC 896)。他解決的問題就是所謂的silly window syndrome,中文稱“愚蠢窗口症候群”,具體含義是,因為普遍終端應用程序每產生一次擊鍵操作就會發送一個包,而典型情況下一個包會擁有一個字節的數據載荷以及40個字節長的包頭,於是產生4000%的過載,很輕易地就能令網絡發生擁塞,。Nagle化后來成了一種標准並且立即在因特網上得以實現。它現在已經成為缺省配置了,但在我們看來,有些場合下把這一選項關掉也是合乎需要的。
現在讓我們假設某個應用程序發出了一個請求,希望發送小塊數據,比如sns游戲中的點擊確定按鈕。我們可以選擇立即發送數據或者等待產生更多的數據然后再一次發送兩種策略。如果我們馬上發送數據,那么交互性的以及客戶/服務器型的應用程序將極大地受益。例如,當我們正在發送一個較短的請求並且等候較大的響應時,相關過載與傳輸的數據總量相比就會比較低,而且,如果請求立即發出那么響應時間也會快一些。以上操作可以通過設置套接字的TCP_NODELAY選項來完成,這樣就禁用了Nagle算法,在nginx中設置tcp_nodelay on,注意放在http標簽里。
總之這種把小包組成大包的操作應該由邏輯層來做
bool SetNoDelay() { int yes = 1; if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) { return false; } return true; }
6 阻塞非阻塞
這個屬性應該是最重要最常用的了。
//1 :non block int SetNonBlock(int value) { #ifndef WIN32 int oldflags = ::fcntl(sock, F_GETFL, 0); /* If reading the flags failed, return error indication now. */ if (oldflags == -1) return -1; /* Set just the flag we want to set. */ if (value != 0) oldflags |= O_NONBLOCK; else oldflags &= ~O_NONBLOCK; /* Store modified flag word in the descriptor. */ return ::fcntl(m_iSock, F_SETFL, oldflags); #else if (::ioctlsocket(sock, FIONBIO, (u_long FAR*)&value) == SOCKET_ERROR) { return -1; } return 0; #endif }
上文中的例子XSock.hpp
原文地址:http://www.fpstop.com/redis/learn_src1/
