Thrift第四課 連接中斷異常處理

場景

        Thrift框架採用了異常處理機制,當客戶端異常斷開連接,服務端這個時候嘗試發送數據給客戶端,Thrift庫會拋出異常,導致進程中斷。這種情況是非常正常的,服務器端應該捕獲異常的發生,但是不應該異常退出。所以應該當前發送數據失敗,直接返回

修改代碼如下:

uint32_t TSocket::write_partial(const uint8_t* buf, uint32_t len) {

  if (socket_ == -1) {

 return -1;

    throw TTransportException(TTransportException::NOT_OPEN, "Called write on non-open socket");

  }


  uint32_t sent = 0;


  int flags = 0;

#ifdef MSG_NOSIGNAL

  // Note the use of MSG_NOSIGNAL to suppress SIGPIPE errors, instead we

  // check for the EPIPE return condition and close the socket in that case

  flags |= MSG_NOSIGNAL;

#endif // ifdef MSG_NOSIGNAL


  int b = send(socket_, const_cast_sockopt(buf + sent), len - sent, flags);

  ++g_socket_syscalls;


  if (b < 0) {

    if (errno == EWOULDBLOCK || errno == EAGAIN) {

      return 0;

    }

    // Fail on a send error

    int errno_copy = errno;

    GlobalOutput.perror("TSocket::write_partial() send() " + getSocketInfo(), errno_copy);


    if (errno_copy == EPIPE || errno_copy == ECONNRESET || errno_copy == ENOTCONN) {

//修改的第一個地方,直接返回-1,不拋出異常

      close();

      return -1;

      //throw TTransportException(TTransportException::NOT_OPEN, "write() send()", errno_copy);

    }

//修改的第二個地方,直接返回-1,不拋出異常

close();

return -1;

    throw TTransportException(TTransportException::UNKNOWN, "write() send()", errno_copy);

  }


  // Fail on blocked send

  if (b == 0) {

    throw TTransportException(TTransportException::NOT_OPEN, "Socket send returned 0.");

  }

  return b;

}


第二種情況,在最外層進行異常捕獲

void TSocket::write(const uint8_t* buf, uint32_t len) {

  uint32_t sent = 0;


  while (sent < len) {

 try

 {

 uint32_t b = write_partial(buf + sent, len - sent);

 if (b == 0) {

 // This should only happen if the timeout set with SO_SNDTIMEO expired.

 // Raise an exception.

 throw TTransportException(TTransportException::TIMED_OUT,

 "send timeout expired");

 }

 sent += b;

 }

 catch (const TTransportException& e)

 {

 return;

 }

  }

}


第三種情況下,在用戶調用的線程進行異常捕獲,這樣子不需要修改代碼

catch (const TTransportException& ttx)

{

//Thrift框架拋出的所有異常都會直接返回

return;

}



分析一下服務器發送數據的函數

void TSocket::write(const uint8_t* buf, uint32_t len) {

  uint32_t sent = 0;


  while (sent < len) {

 uint32_t b = write_partial(buf + sent, len - sent);

 if (b == 0) {

 // This should only happen if the timeout set with SO_SNDTIMEO expired.

 // Raise an exception.

 throw TTransportException(TTransportException::TIMED_OUT,

 "send timeout expired");

 }

 sent += b;

  }

}

但b==0拋出異常,代表當前發送超時。while循環是爲了循環發送,因爲一次不一定發送完用戶數據,畢竟MTU的限制。注意sent是一個無符號整型,當b返回-1的時候,sent==0-1意味着將達到32位整數最大值,大於len,從而直接退出循環。因爲套接字已經中斷,所以發送失敗,在調用write_partial函數的時候,返回b ==-1,導致退出循環,從而避免了拋出異常,因此返回-1,是非常合理的值



問題

當用戶異常中斷連接,Thrift框架是如何捕獲到該中斷信號的,暫時不清楚。

TThreadPoolServer.cpp  void run()函數中斷


if (!processor_->process(input_, output_, connectionContext)||

!input_->getTransport()->peek()) 

當前函數處理髮送的數據包







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章