- int ret = this->peer().send_n(ack_msg, len, &time_zero_);
- switch (ret)
- {
- case 0:
- // peer closed
-
- return -1;
- case -1:
- if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINPROGRESS)
- {
- //block
- return 0;
- }
- else
- {
- // abnormal error
- }
- default:
- if (ret != len)
- {
- //no complex
- return -1;
- }
- break;
- if ((r = write(fd, offset, toSend)) < 0) {
- switch (errno) {
- case EAGAIN:
- case EINTR:
- r = 0;
- break;
- case EPIPE:
- case ECONNRESET:
- return -2;
- default:
- log_error_write(srv, __FILE__, __LINE__, "ssd",
- "write failed:", strerror(errno), fd);
- return -1;
- }
- }
- c->offset += r;
- cq->bytes_out += r;
- if (c->offset == (off_t)c->mem->used - 1) {
- chunk_finished = 1;
- }
在Unix系統(tǒng)下,如果send
、
recv
、
write在等待協(xié)議傳送數(shù)據(jù)時(shí)
,
socket
被
shutdown,調(diào)用send的進(jìn)程會(huì)接收到一個(gè)SIGPIPE信號(hào),進(jìn)程對(duì)該信號(hào)的默認(rèn)處理是進(jìn)程終止。
此種情況
應(yīng)用就很難查
出
處理進(jìn)程為什么退出。
SIGPIPE
信號(hào):
對(duì)一個(gè)已經(jīng)收到FIN包的socket調(diào)用read方法, 如果接收緩沖已空, 則返回0, 這就是常說(shuō)的表示連接關(guān)閉. 但第一次對(duì)其調(diào)用write方法時(shí), 如果發(fā)送緩沖沒(méi)問(wèn)題, 會(huì)返回正確寫入(發(fā)送). 但發(fā)送的報(bào)文會(huì)導(dǎo)致對(duì)端發(fā)送RST報(bào)文, 因?yàn)閷?duì)端的socket已經(jīng)調(diào)用了close, 完全關(guān)閉, 既不發(fā)送, 也不接收數(shù)據(jù). 所以, 第二次調(diào)用write方法(假設(shè)在收到RST之后), 會(huì)生成SIGPIPE信號(hào), 導(dǎo)致進(jìn)程退出
。如果對(duì)
SIGPIPE
進(jìn)行忽略處理,
二次調(diào)用write方法時(shí), 會(huì)返回-1, 同時(shí)errno置為SIGPIPE.
處理方法:
在初始化時(shí)調(diào)用
signal(SIGPIPE,SIG_IGN)
忽略該信號(hào)(只需一次)
,
SIGPIPE交給了系統(tǒng)處理。
此時(shí)
send
、
recv
或
write
函數(shù)將返回-1,errno為EPIPE,可視情況關(guān)閉socket或其他處理
SIGPIPE
被忽略的情況下,如果
服務(wù)器采用了fork的話,要收集垃圾進(jìn)程,防止僵尸進(jìn)程的產(chǎn)生,可以這樣處理: signal(SIGCHLD,SIG_IGN); 交給系統(tǒng)init去回收。
這樣
子進(jìn)程就不會(huì)產(chǎn)生僵尸進(jìn)程了。
ACE中發(fā)送和接收超時(shí)都是基于select的
- ssize_t
- ACE::send_n_i (ACE_HANDLE handle,
- const void *buf,
- size_t len,
- int flags,
- const ACE_Time_Value *timeout,
- size_t *bt)
- {
- size_t temp;
- size_t &bytes_transferred = bt == 0 ? temp : *bt;
- ssize_t n;
- ssize_t result = 0;
- int error = 0;
- int val = 0;
- ACE::record_and_set_non_blocking_mode (handle, val);
- for (bytes_transferred = 0;
- bytes_transferred < len;
- bytes_transferred += n)
- {
- // Try to transfer as much of the remaining data as possible.
- // Since the socket is in non-blocking mode, this call will not
- // block.
- n = ACE_OS::send (handle,
- (char *) buf + bytes_transferred,
- len - bytes_transferred,
- flags);
- // Check for errors.
- if (n == 0 ||
- n == -1)
- {
- // Check for possible blocking.
- if (n == -1 &&
- (errno == EWOULDBLOCK || errno == ENOBUFS))
- {
- // Wait upto <timeout> for the blocking to subside.
- int rtn = ACE::handle_write_ready (handle,
- timeout);
- // Did select() succeed?
- if (rtn != -1)
- {
- // Blocking subsided in <timeout> period. Continue
- // data transfer.
- n = 0;
- continue;
- }
- }
- // Wait in select() timed out or other data transfer or
- // select() failures.
- error = 1;
- result = n;
- break;
- }
- }
- ACE::restore_non_blocking_mode (handle, val);
- if (error)
- {
- return result;
- }
- else
- {
- return ACE_Utils::truncate_cast<ssize_t> (bytes_transferred);
- }
- }
- int
- ACE::handle_ready (ACE_HANDLE handle,
- const ACE_Time_Value *timeout,
- int read_ready,
- int write_ready,
- int exception_ready)
- {
- #if defined (ACE_HAS_POLL) && defined (ACE_HAS_LIMITED_SELECT)
- ACE_UNUSED_ARG (write_ready);
- ACE_UNUSED_ARG (exception_ready);
- struct pollfd fds;
- fds.fd = handle;
- fds.events = read_ready ? POLLIN : POLLOUT;
- fds.revents = 0;
- int result = ACE_OS::poll (&fds, 1, timeout);
- #else
- ACE_Handle_Set handle_set;
- handle_set.set_bit (handle);
- // Wait for data or for the timeout to elapse.
- int select_width;
- # if defined (ACE_WIN32)
- // This arg is ignored on Windows and causes pointer truncation
- // warnings on 64-bit compiles.
- select_width = 0;
- # else
- select_width = int (handle) + 1;
- # endif /* ACE_WIN64 */
- int result = ACE_OS::select (select_width,
- read_ready ? handle_set.fdset () : 0, // read_fds.
- write_ready ? handle_set.fdset () : 0, // write_fds.
- exception_ready ? handle_set.fdset () : 0, // exception_fds.
- timeout);
- #endif /* ACE_HAS_POLL && ACE_HAS_LIMITED_SELECT */
- switch (result)
- {
- case 0: // Timer expired.
- errno = ETIME;
- /* FALLTHRU */
- case -1: // we got here directly - select() returned -1.
- return -1;
- case 1: // Handle has data.
- /* FALLTHRU */
- default: // default is case result > 0; return a
- // ACE_ASSERT (result == 1);
- return result;
- }
其它lighttpd:
- // if (len == 0 || (len < 0 && errno != EAGAIN && errno != EINTR) ) {
- case -1:
- if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINPROGRESS)
- {
- return 0;
- }
- if (-1 == (cnt = accept(srv_socket->fd, (struct sockaddr *) &cnt_addr, &cnt_len))) {
- switch (errno) {
- case EAGAIN:
- #if EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK:
- #endif
- case EINTR:
- /* we were stopped _before_ we had a connection */
- case ECONNABORTED: /* this is a FreeBSD thingy */
- /* we were stopped _after_ we had a connection */
- break;
- case EMFILE:
- /* out of fds */
- break;
- default:
- log_error_write(srv, __FILE__, __LINE__, "ssd", "accept failed:", strerror(errno), errno);
- }
- return NULL;
- }
- fcgi
- if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) {
- if (errno == EINPROGRESS ||
- errno == EALREADY ||
- errno == EINTR) {
- if (hctx->conf.debug > 2) {
- log_error_write(srv, __FILE__, __LINE__, "sb",
- "connect delayed; will continue later:", proc->connection_name);
- }
- return CONNECTION_DELAYED;
- } else if (errno == EAGAIN) {
- if (hctx->conf.debug) {
- log_error_write(srv, __FILE__, __LINE__, "sbsd",
- "This means that you have more incoming requests than your FastCGI backend can handle in parallel."
- "It might help to spawn more FastCGI backends or PHP children; if not, decrease server.max-connections."
- "The load for this FastCGI backend", proc->connection_name, "is", proc->load);
- }
- return CONNECTION_OVERLOADED;
- } else {
- log_error_write(srv, __FILE__, __LINE__, "sssb",
- "connect failed:",
- strerror(errno), "on",
- proc->connection_name);
- return CONNECTION_DEAD;
- }
- }
if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0)
EAGAIN、EWOULDBLOCK、EINTR與非阻塞 長(zhǎng)連接
EWOULDBLOCK用于非阻塞模式,不需要重新讀或者寫
EINTR指操作被中斷喚醒,需要重新讀/寫
在Linux環(huán)境下開發(fā)經(jīng)常會(huì)碰到很多錯(cuò)誤(設(shè)置errno),其中EAGAIN是其中比較常見的一個(gè)錯(cuò)誤(比如用在非阻塞操作中)。
從字面上來(lái)看,是提示再試一次。這個(gè)錯(cuò)誤經(jīng)常出現(xiàn)在當(dāng)應(yīng)用程序進(jìn)行一些非阻塞(non-blocking)操作(對(duì)文件或socket)的時(shí)候。例如,以
O_NONBLOCK的標(biāo)志打開文件/socket/FIFO,如果你連續(xù)做read操作而沒(méi)有數(shù)據(jù)可讀。此時(shí)程序不會(huì)阻塞起來(lái)等待數(shù)據(jù)準(zhǔn)備就緒返
回,read函數(shù)會(huì)返回一個(gè)錯(cuò)誤EAGAIN,提示你的應(yīng)用程序現(xiàn)在沒(méi)有數(shù)據(jù)可讀請(qǐng)稍后再試。
又例如,當(dāng)一個(gè)系統(tǒng)調(diào)用(比如fork)因?yàn)闆](méi)有足夠的資源(比如虛擬內(nèi)存)而執(zhí)行失敗,返回EAGAIN提示其再調(diào)用一次(也許下次就能成功)。
Linux - 非阻塞socket編程處理EAGAIN錯(cuò)誤
在linux進(jìn)行非阻塞的socket接收數(shù)據(jù)時(shí)經(jīng)常出現(xiàn)Resource temporarily unavailable,errno代碼為11(EAGAIN),這是什么意思?
這表明你在非阻塞模式下調(diào)用了阻塞操作,在該操作沒(méi)有完成就返回這個(gè)錯(cuò)誤,這個(gè)錯(cuò)誤不會(huì)破壞socket的同步,不用管它,下次循環(huán)接著recv就可以。
對(duì)非阻塞socket而言,EAGAIN不是一種錯(cuò)誤。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。
另外,如果出現(xiàn)EINTR即errno為4,錯(cuò)誤描述Interrupted system call,操作也應(yīng)該繼續(xù)。
最后,如果recv的返回值為0,那表明連接已經(jīng)斷開,我們的接收操作也應(yīng)該結(jié)束。
當(dāng)客戶通過(guò)Socket提供的send函數(shù)發(fā)送大的數(shù)據(jù)包時(shí),就可能返回一個(gè)EGGAIN的錯(cuò)誤。該錯(cuò)誤產(chǎn)生的原因是由于send
函數(shù)
中的size變量大小超過(guò)了tcp_sendspace的值。tcp_sendspace定義了應(yīng)用在調(diào)用send之前能夠在kernel中緩存的數(shù)據(jù)
量。當(dāng)應(yīng)用程序在socket中設(shè)置了O_NDELAY或者O_NONBLOCK屬性后,如果發(fā)送緩存被占滿,send就會(huì)返回EAGAIN的錯(cuò)誤。
為了消除該錯(cuò)誤,有三種方法可以選擇:
1.調(diào)大tcp_sendspace,使之大于send中的size參數(shù)
---no -p -o tcp_sendspace=65536
2.在調(diào)用send前,在setsockopt函數(shù)中為SNDBUF設(shè)置更大的值
1.你自己的緩沖區(qū)滿了,會(huì)返回EAGAIN。
2.你的沒(méi)滿,對(duì)方的緩沖區(qū)滿了,肯定不關(guān)你事,可能會(huì)發(fā)送不成功,但是協(xié)議棧提供的系統(tǒng)調(diào)用,只管數(shù)據(jù)成功從你的緩沖區(qū)發(fā)出去,之后人家因?yàn)榫彌_區(qū)滿收不到數(shù)據(jù),tcp自己有重傳機(jī)制(參考Tcp/ip詳解卷1)。
|