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

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

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

    中文JAVA技術(shù)平等自由協(xié)作創(chuàng)造

    Java專題文章博客和開(kāi)源

    常用鏈接

    統(tǒng)計(jì)

    最新評(píng)論

    java 中的connection reset 異常處理分析

      在Java中常看見(jiàn)的幾個(gè)connection rest exception, Broken pipe, Connection reset,Connection reset by peer
     
       Socked reset case
     
       Linux中會(huì)有2個(gè)常見(jiàn)的sock reset 情況下的錯(cuò)誤代碼
     
       ECONNRESET
     
       該錯(cuò)誤被描述為“connection reset by peer”,即“對(duì)方復(fù)位連接”,這種情況一般發(fā)生在服務(wù)進(jìn)程較客戶進(jìn)程提前終止。當(dāng)服務(wù)進(jìn)程終止時(shí)會(huì)向客戶 TCP 發(fā)送 FIN 分節(jié),客戶 TCP 回應(yīng) ACK,服務(wù) TCP 將轉(zhuǎn)入 FIN_WAIT2 狀態(tài)。此時(shí)如果客戶進(jìn)程沒(méi)有處理該 FIN (如阻塞在其它調(diào)用上而沒(méi)有關(guān)閉 Socket 時(shí)),則客戶 TCP 將處于 CLOSE_WAIT 狀態(tài)。當(dāng)客戶進(jìn)程再次向 FIN_WAIT2 狀態(tài)的服務(wù) TCP 發(fā)送數(shù)據(jù)時(shí),則服務(wù) TCP 將立刻響應(yīng) RST。一般來(lái)說(shuō),這種情況還可以會(huì)引發(fā)另外的應(yīng)用程序異常,客戶進(jìn)程在發(fā)送完數(shù)據(jù)后,往往會(huì)等待從網(wǎng)絡(luò)IO接收數(shù)據(jù),很典型的如 read 或 readline 調(diào)用,此時(shí)由于執(zhí)行時(shí)序的原因,如果該調(diào)用發(fā)生在 RST 分節(jié)收到前執(zhí)行的話,那么結(jié)果是客戶進(jìn)程會(huì)得到一個(gè)非預(yù)期的 EOF 錯(cuò)誤。此時(shí)一般會(huì)輸出“server terminated prematurely”-“服務(wù)器過(guò)早終止”錯(cuò)誤。
     
       EPIPE
     
       錯(cuò)誤被描述為“broken pipe”,即“管道破裂”,這種情況一般發(fā)生在客戶進(jìn)程不理會(huì)(或未及時(shí)處理)Socket 錯(cuò)誤,繼續(xù)向服務(wù) TCP 寫(xiě)入更多數(shù)據(jù)時(shí),內(nèi)核將向客戶進(jìn)程發(fā)送 SIGPIPE 信號(hào),該信號(hào)默認(rèn)會(huì)使進(jìn)程終止(此時(shí)該前臺(tái)進(jìn)程未進(jìn)行 core dump)。結(jié)合上邊的 ECONNRESET 錯(cuò)誤可知,向一個(gè) FIN_WAIT2 狀態(tài)的服務(wù) TCP(已 ACK 響應(yīng) FIN 分節(jié))寫(xiě)入數(shù)據(jù)不成問(wèn)題,但是寫(xiě)一個(gè)已接收了 RST 的 Socket 則是一個(gè)錯(cuò)誤。
     
       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");
     
       }
     
       可以看到j(luò)ava 在讀和寫(xiě)的情況關(guān)于EPIPE的情況是處理不一樣的
     
       在read 的情況中,Reset 是全部拋出 ConnectionResetException, 提示的錯(cuò)誤信息是 Connection Reset
     
       在write的情況下,Reset 對(duì)ECONNRESET的是拋出ConnectionResetException, 而對(duì)EPIPE 拋出的是SocketException ,錯(cuò)誤信息是Broken pipe
     
       如何打印出信息Broken pipe
     
       SIGPIPE信號(hào)處理函數(shù)
     
       當(dāng)在收到reset包后,如果在讀寫(xiě)socket,會(huì)出現(xiàn)錯(cuò)誤EPIPE,同時(shí)經(jīng)常收到SIGPIPE信號(hào)雅思改分
     
       在程序中可以看到j(luò)ava 并沒(méi)有對(duì)write的情況下沒(méi)有處理錯(cuò)誤EPIPE,開(kāi)始的時(shí)候錯(cuò)誤的以拋出的異常是信號(hào)處理函數(shù)拋出的雅思答案
     
       先來(lái)看一下關(guān)于信號(hào)SIGPIPE的處理函數(shù),在Linux::install_signal_handlers 里面調(diào)用函數(shù)
     
       [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);
     
       而函數(shù)set_signal_handler,中對(duì)對(duì)應(yīng)的信號(hào)處理函數(shù)是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;
     
       }
     
       最終還是調(diào)用了函數(shù) JVM_handle_linux_signal
     
       在X86架構(gòu)下, 函數(shù)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;
     
       }
     
       }
     
       ...
     
       }
     
       對(duì)信號(hào)SIGPIPE 使用了chained handler處理,也就是使用了系統(tǒng)的原來(lái)信號(hào)處理函數(shù),也就證明了異常并不是信號(hào)處理函數(shù)拋出的
     
       NET_ThrowByNameWithLastError函數(shù)
     
       既然不是信號(hào)處理函數(shù)拋出的異常,繼續(xù)查看原來(lái)的outputstream的程序
     
       [cpp]
     
       if (errno == ECONNRESET) {
     
       JNU_ThrowByName(env, "sun/net/ConnectionResetException",
     
       "Connection reset");
     
       } else {
     
       NET_ThrowByNameWithLastError(env, "java/net/SocketException",
     
       "Write failed");
     
       }
     
       也就是else 的情況,那么針對(duì)EPIPE的錯(cuò)誤,java拋出的socketexception, 錯(cuò)誤信息是Write failed ,事實(shí)上我們可以看到的卻是SockedException,異常對(duì)對(duì)上了, 但信息顯示是Broken pipe,而不是Write failed.
     
       關(guān)鍵點(diǎn)就在函數(shù) 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);
     
       }
     
       函數(shù)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的異常信息,也就是開(kāi)始對(duì)應(yīng)的Write failed!
     
       JVM_GetLastErrorString 使用hpi::lasterror ,也就是函數(shù)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;
     
       }
     
       }
     
       原來(lái)是strerror(errno) ,也就是直接顯示linux kernel 對(duì)應(yīng)這個(gè)error number 的錯(cuò)誤內(nèi)容
     
       結(jié)論:Broken pipe 是內(nèi)核對(duì)應(yīng)的錯(cuò)誤信息,并不是java自己提供的信息
     
     

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

    評(píng)論

    # re: java 中的connection reset 異常處理分析 2013-07-30 08:42 開(kāi)發(fā)吧

    謝謝分享!  回復(fù)  更多評(píng)論   


    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    PK10開(kāi)獎(jiǎng) PK10開(kāi)獎(jiǎng)
    主站蜘蛛池模板: 亚洲国产女人aaa毛片在线 | 99精品视频在线观看免费播放| 国产裸模视频免费区无码| 羞羞的视频在线免费观看| 白白国产永久免费视频| 一级特黄a免费大片| 亚洲网站在线观看| 午夜小视频免费观看| 亚洲综合无码无在线观看| 国产伦一区二区三区免费| 亚洲jizzjizz少妇| 处破痛哭A√18成年片免费| eeuss免费天堂影院| 亚洲乱码中文字幕小综合| jizzjizz亚洲| 午夜性色一区二区三区免费不卡视频| 亚洲黄网在线观看| 67194成手机免费观看| 亚洲狠狠综合久久| 18禁美女裸体免费网站| 亚洲13又紧又嫩又水多| 亚洲午夜福利717| 日日AV拍夜夜添久久免费| 午夜亚洲国产精品福利| 亚洲欧洲日韩国产| 亚洲中文字幕无码一区| 青青草国产免费久久久下载| 久久久久免费看黄a级试看| 亚洲视频在线一区二区三区| 亚洲熟伦熟女新五十路熟妇 | 亚洲精品无码久久久久秋霞| 亚洲午夜无码久久久久| 亚洲毛片在线免费观看| 亚洲日韩在线观看免费视频| 亚洲国产精品18久久久久久| 免费在线观看污网站| 成人无码区免费A片视频WWW| 最新亚洲人成无码网www电影| 免费又黄又爽又猛的毛片 | 日本在线看片免费| 黄色网页在线免费观看|