<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    中文JAVA技術平等自由協作創造

    Java專題文章博客和開源

    常用鏈接

    統計

    最新評論

    java 中的connection reset 異常處理分析

      在Java中常看見的幾個connection rest exception, Broken pipe, Connection reset,Connection reset by peer
     
       Socked reset case
     
       Linux中會有2個常見的sock reset 情況下的錯誤代碼
     
       ECONNRESET
     
       該錯誤被描述為“connection reset by peer”,即“對方復位連接”,這種情況一般發生在服務進程較客戶進程提前終止。當服務進程終止時會向客戶 TCP 發送 FIN 分節,客戶 TCP 回應 ACK,服務 TCP 將轉入 FIN_WAIT2 狀態。此時如果客戶進程沒有處理該 FIN (如阻塞在其它調用上而沒有關閉 Socket 時),則客戶 TCP 將處于 CLOSE_WAIT 狀態。當客戶進程再次向 FIN_WAIT2 狀態的服務 TCP 發送數據時,則服務 TCP 將立刻響應 RST。一般來說,這種情況還可以會引發另外的應用程序異常,客戶進程在發送完數據后,往往會等待從網絡IO接收數據,很典型的如 read 或 readline 調用,此時由于執行時序的原因,如果該調用發生在 RST 分節收到前執行的話,那么結果是客戶進程會得到一個非預期的 EOF 錯誤。此時一般會輸出“server terminated prematurely”-“服務器過早終止”錯誤。
     
       EPIPE
     
       錯誤被描述為“broken pipe”,即“管道破裂”,這種情況一般發生在客戶進程不理會(或未及時處理)Socket 錯誤,繼續向服務 TCP 寫入更多數據時,內核將向客戶進程發送 SIGPIPE 信號,該信號默認會使進程終止(此時該前臺進程未進行 core dump)。結合上邊的 ECONNRESET 錯誤可知,向一個 FIN_WAIT2 狀態的服務 TCP(已 ACK 響應 FIN 分節)寫入數據不成問題,但是寫一個已接收了 RST 的 Socket 則是一個錯誤。
     
       Java 中的socket input stream/output stream 的處理
     
       先看代碼片段
     
       SocketInputStream.c
     
       [cpp]
     
       switch (errno) {
     
       case ECONNRESET:
     
       case EPIPE:
     
       JNU_ThrowByName(env, "sun/net/ConnectionResetException",
     
       "Connection reset");
     
       break;
     
       ....
     
       SocketOutputStream.c
     
       [cpp]
     
       if (errno == ECONNRESET) {
     
       JNU_ThrowByName(env, "sun/net/ConnectionResetException",
     
       "Connection reset");
     
       } else {
     
       NET_ThrowByNameWithLastError(env, "java/net/SocketException",
     
       "Write failed");
     
       }
     
       可以看到java 在讀和寫的情況關于EPIPE的情況是處理不一樣的
     
       在read 的情況中,Reset 是全部拋出 ConnectionResetException, 提示的錯誤信息是 Connection Reset
     
       在write的情況下,Reset 對ECONNRESET的是拋出ConnectionResetException, 而對EPIPE 拋出的是SocketException ,錯誤信息是Broken pipe
     
       如何打印出信息Broken pipe
     
       SIGPIPE信號處理函數
     
       當在收到reset包后,如果在讀寫socket,會出現錯誤EPIPE,同時經常收到SIGPIPE信號雅思改分
     
       在程序中可以看到java 并沒有對write的情況下沒有處理錯誤EPIPE,開始的時候錯誤的以拋出的異常是信號處理函數拋出的雅思答案
     
       先來看一下關于信號SIGPIPE的處理函數,在Linux::install_signal_handlers 里面調用函數
     
       [cpp]
     
       set_signal_handler(SIGSEGV, true);
     
       set_signal_handler(SIGPIPE, true);
     
       set_signal_handler(SIGBUS, true);
     
       set_signal_handler(SIGILL, true);
     
       set_signal_handler(SIGFPE, true);
     
       set_signal_handler(SIGXFSZ, true);
     
       而函數set_signal_handler,中對對應的信號處理函數是signalHandler
     
       [cpp]
     
       sigAct.sa_handler = SIG_DFL;
     
       if (!set_installed) {
     
       sigAct.sa_flags = SA_SIGINFO|SA_RESTART;
     
       } else {
     
       sigAct.sa_sigaction = signalHandler;
     
       sigAct.sa_flags = SA_SIGINFO|SA_RESTART;
     
       }
     
       最終還是調用了函數 JVM_handle_linux_signal
     
       在X86架構下, 函數JVM_handle_linux_signal
     
       [cpp]
     
       extern "C" int
     
       JVM_handle_linux_signal(int sig,
     
       siginfo_t* info,
     
       void* ucVoid,
     
       int abort_if_unrecognized) {
     
       ucontext_t* uc = (ucontext_t*) ucVoid;
     
       Thread* t = ThreadLocalStorage::get_thread_slow();
     
       SignalHandlerMark shm(t);
     
       // Note: it's not uncommon that JNI code uses signal/sigset to install
     
       // then restore certain signal handler (e.g. to temporarily block SIGPIPE,
     
       // or have a SIGILL handler when detecting CPU type). When that happens,
     
       // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To
     
       // avoid unnecessary crash when libjsig is not preloaded, try handle signals
     
       // that do not require siginfo/ucontext first.
     
       if (sig == SIGPIPE || sig == SIGXFSZ) {
     
       // allow chained handler to go first
     
       if (os::Linux::chained_handler(sig, info, ucVoid)) {
     
       return true;
     
       } else {
     
       if (PrintMiscellaneous && (WizardMode || Verbose)) {
     
       char buf[64];
     
       warning("Ignoring %s - see bugs 4229104 or 646499219",
     
       os::exception_name(sig, buf, sizeof(buf)));
     
       }
     
       return true;
     
       }
     
       }
     
       ...
     
       }
     
       對信號SIGPIPE 使用了chained handler處理,也就是使用了系統的原來信號處理函數,也就證明了異常并不是信號處理函數拋出的
     
       NET_ThrowByNameWithLastError函數
     
       既然不是信號處理函數拋出的異常,繼續查看原來的outputstream的程序
     
       [cpp]
     
       if (errno == ECONNRESET) {
     
       JNU_ThrowByName(env, "sun/net/ConnectionResetException",
     
       "Connection reset");
     
       } else {
     
       NET_ThrowByNameWithLastError(env, "java/net/SocketException",
     
       "Write failed");
     
       }
     
       也就是else 的情況,那么針對EPIPE的錯誤,java拋出的socketexception, 錯誤信息是Write failed ,事實上我們可以看到的卻是SockedException,異常對對上了, 但信息顯示是Broken pipe,而不是Write failed.
     
       關鍵點就在函數 NET_ThrowByNameWithLastError
     
       [cpp]
     
       void
     
       NET_ThrowByNameWithLastError(JNIEnv *env, const char *name,
     
       const char *defaultDetail) {
     
       char errmsg[255];
     
       sprintf(errmsg, "errno: %d, error: %s\n", errno, defaultDetail);
     
       JNU_ThrowByNameWithLastError(env, name, errmsg);
     
       }
     
       函數JNU_ThrowByNameWithLastError
     
       [cpp]
     
       JNIEXPORT void JNICALL
     
       JNU_ThrowByNameWithLastError(JNIEnv *env, const char *name,
     
       const char *defaultDetail)
     
       {
     
       char buf[256];
     
       int n = JVM_GetLastErrorString(buf, sizeof(buf));
     
       if (n > 0) {
     
       jstring s = JNU_NewStringPlatform(env, buf);
     
       if (s != NULL) {
     
       jobject x = JNU_NewObjectByName(env, name,
     
       "(Ljava/lang/String;)V", s);
     
       if (x != NULL) {
     
       (*env)->Throw(env, x);
     
       }
     
       }
     
       }
     
       if (!(*env)->ExceptionOccurred(env)) {
     
       JNU_ThrowByName(env, name, defaultDetail);
     
       }
     
       }
     
       程序可以看到先顯示 JVM_GetLastErrorString 的信息,如果信息是空的情況下才顯示defaultDetail的異常信息,也就是開始對應的Write failed!
     
       JVM_GetLastErrorString 使用hpi::lasterror ,也就是函數sysGetLastErrorString 在linux和solaris 是一樣的
     
       [cpp]
     
       int
     
       sysGetLastErrorString(char *buf, int len)
     
       {
     
       if (errno == 0) {
     
       return 0;
     
       } else {
     
       const char *s = strerror(errno);
     
       int n = strlen(s);
     
       if (n >= len) n = len - 1;
     
       strncpy(buf, s, n);
     
       buf[n] = '\0';
     
       return n;
     
       }
     
       }
     
       原來是strerror(errno) ,也就是直接顯示linux kernel 對應這個error number 的錯誤內容
     
       結論:Broken pipe 是內核對應的錯誤信息,并不是java自己提供的信息
     
     

    posted on 2013-07-28 18:35 好不容易 閱讀(464) 評論(1)  編輯  收藏

    評論

    # re: java 中的connection reset 異常處理分析 2013-07-30 08:42 開發吧

    謝謝分享!  回復  更多評論   


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    PK10開獎 PK10開獎
    主站蜘蛛池模板: 成人黄软件网18免费下载成人黄18免费视频 | 91精品成人免费国产| 国产又大又黑又粗免费视频| 亚洲日本国产综合高清| 无码国产精品一区二区免费I6| 亚洲国产成人综合| 精品国产sm捆绑最大网免费站 | 国产精品成人免费综合| 亚洲av日韩aⅴ无码色老头 | 中文字幕亚洲一区| 国精产品一区一区三区免费视频| 亚洲综合av永久无码精品一区二区| 久久久久久av无码免费看大片| 亚洲精品午夜国产VA久久成人 | 亚洲性色AV日韩在线观看| 免费黄色一级毛片| 日本一区二区三区免费高清在线| 亚洲精品国产福利一二区| 国产99精品一区二区三区免费| 久久国产亚洲精品麻豆| 2021免费日韩视频网| 亚洲丰满熟女一区二区哦| 亚洲国产成人精品女人久久久| 日本免费网站视频www区| 亚洲激情校园春色| 好吊妞788免费视频播放| 亚洲aⅴ无码专区在线观看春色| 国产精品亚洲w码日韩中文| 嫩草在线视频www免费观看| 亚洲AV无码乱码在线观看代蜜桃| 热久久精品免费视频| 中文在线免费看视频| 亚洲成aⅴ人在线观看| 国产成人免费一区二区三区| 黄色网站软件app在线观看免费| 亚洲欧洲日产国码在线观看| 国产精品高清全国免费观看| 在线人成免费视频69国产| 久久亚洲国产成人影院| 亚洲中文字幕在线观看| 最新猫咪www免费人成|