我正在C中編寫一個TCP服務器,並發現一些不尋常的事情發生一旦聽力FD得到「太多打開的文件」錯誤。 accept
調用不再阻止,並且始終返回-1。TCP服務器 - 從「太多打開的文件」恢復
我也嘗試關閉收聽fd並重新打開,重新綁定它,但似乎沒有工作。
我的問題是,爲什麼accept
保持返回-1在這種情況下,我應該怎樣做才能阻止它,使服務器能夠接受新的連接任何老客戶關閉後? (插座當然能夠accept
時再正確一些連接關閉)
====== UPDATE:澄清======
只是因爲有效客戶的數量是出現該問題超過了開放文件系統的限制,所以我並沒有close
示例代碼中的任何公認fds,只是爲了讓它更快地複製。
我每次添加時間戳accept
返回輸出,減緩connect
頻率曾經在2秒鐘,然後我發現其實在最新的成功accept
之後立即發生了「打開的文件太多」錯誤。所以我認爲這是因爲當maxium fds達到時,每個accept
的調用都會立即返回,返回值是-1。 (我認爲accept
仍然會阻止,但在下一次傳入connect
時返回-1。在這種情況下accept
的行爲是我自己的理論,而不是來自手冊頁,如果它是錯誤的,請讓我知道)。
因此,對於我的第二個問題,爲了讓它停止,我認爲這是一個解決方案,在任何連接是close
d之前停止呼叫accept
。
也更新示例代碼。謝謝你的幫助。
======示例代碼======
以下是我如何測試它。首先將ulimit -n
設置爲一個較低的值(如16)並運行由以下C源代碼編譯的服務器程序;然後使用Python腳本來創建幾個連接
/* TCP server; bind :5555 */
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFSIZE 1024
#define PORT 5555
void error(char const* msg)
{
perror(msg);
exit(1);
}
int listen_port(int port)
{
int parentfd; /* parent socket */
struct sockaddr_in serveraddr; /* server's addr */
int optval; /* flag value for setsockopt */
parentfd = socket(AF_INET, SOCK_STREAM, 0);
if (parentfd < 0) {
error("ERROR opening socket");
}
optval = 1;
setsockopt(parentfd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&optval , sizeof(int));
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if (bind(parentfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) {
error("ERROR on binding");
}
if (listen(parentfd, 5) < 0) {
error("ERROR on listen");
}
printf("Listen :%d\n", port);
return parentfd;
}
int main(int argc, char **argv)
{
int parentfd; /* parent socket */
int childfd; /* child socket */
int clientlen; /* byte size of client's address */
struct sockaddr_in clientaddr; /* client addr */
int accept_count; /* times of accept called */
accept_count = 0;
parentfd = listen_port(PORT);
clientlen = sizeof(clientaddr);
while (1) {
childfd = accept(parentfd, (struct sockaddr *) &clientaddr, (socklen_t*) &clientlen);
printf("accept returns ; count=%d ; time=%u ; fd=%d\n", accept_count++, (unsigned) time(NULL), childfd);
if (childfd < 0) {
perror("error on accept");
/* the following 2 lines try to close the listening fd and re-open it */
// close(parentfd);
// parentfd = listen_port(PORT);
// the following line let the program exit at the first error
error("--- error on accept");
}
}
}
Python程序來創建連接
import time
import socket
def connect(host, port):
s = socket.socket()
s.connect((host, port))
return s
if __name__ == '__main__':
socks = []
try:
try:
for i in xrange(100):
socks.append(connect('127.0.0.1', 5555))
print ('connect count: ' + str(i))
time.sleep(2)
except IOError as e:
print ('error: ' + str(e))
print ('stop')
while True:
time.sleep(10)
except KeyboardInterrupt:
for s in socks:
s.close()
我注意到您的示例代碼根本不涉及客戶端。在評論中你說這是爲了可讀性......但這也可能隱藏了這個問題。例如,如果您調用'fork',則需要記住關閉** all **進程上的連接(只有當所有打開的句柄關閉時,連接纔會被複制並關閉)。使用您的示例代碼無法檢查這些問題。現在,您只需要在客戶端調用close,就像我之前的其他人所說的那樣。 – Myst