2013-02-25 14 views
5

golang TCPConn.Writeによって返されたエラーを検査して送信失敗を検出しようとしていますが、それはゼロです。私はまた、成功なしでTCPConn.SetWriteDeadlineを使ってみました。golang TCPConn.SetWriteDeadlineが期待通りに動作しないようです。

物事が起こる方法は次のとおりです。

  1. サーバが
  2. クライアントが
  3. サーバーがメッセージを送信し、クライアントは、クライアント
  4. シャットダウンし、それ
  5. 受け取りを接続を開始サーバーはもう1つのメッセージを送信します。エラーなし
  6. サーバーは3番目のメッセージを送信しますGE:今だけのエラーが表示されます。

質問:なぜ唯一のエラーで非既存のクライアント結果の2番目のメッセージ?ケースは適切にどのように扱われるべきですか?

コードは次のとおりです。

package main 

import (
    "net" 
    "os" 
    "bufio" 
    "fmt" 
    "time" 
) 

func AcceptConnections(listener net.Listener, console <- chan string) { 

    msg := "" 

    for { 

     conn, err := listener.Accept() 

     if err != nil { 
      panic(err) 
     } 

     fmt.Printf("client connected\n") 

     for { 

      if msg == "" { 
       msg = <- console 
       fmt.Printf("read from console: %s", msg) 
      } 

      err = conn.SetWriteDeadline(time.Now().Add(time.Second)) 

      if err != nil { 
       fmt.Printf("SetWriteDeadline failed: %v\n", err) 
      } 

      _, err = conn.Write([]byte(msg)) 

      if err != nil { 
       // expecting an error after sending a message 
       // to a non-existing client endpoint 
       fmt.Printf("failed sending a message to network: %v\n", err) 
       break 
      } else { 
       fmt.Printf("msg sent: %s", msg) 
       msg = "" 
      } 
     } 
    } 
} 

func ReadConsole(network chan <- string) { 

    console := bufio.NewReader(os.Stdin) 

    for { 

     line, err := console.ReadString('\n') 

     if err != nil { 

      panic(err) 

     } else { 

      network <- line 
     } 
    } 
} 

func main() { 

    listener, err := net.Listen("tcp", "localhost:6666") 

    if err != nil { 
     panic(err) 
    } 

    println("listening on " + listener.Addr().String()) 

    consoleToNetwork := make(chan string) 

    go AcceptConnections(listener, consoleToNetwork) 

    ReadConsole(consoleToNetwork) 
} 

サーバーコンソールは、次のようになります。

listening on 127.0.0.1:6666 
client connected 
hi there! 
read from console: hi there! 
msg sent: hi there! 
this one should fail 
read from console: this one should fail 
msg sent: this one should fail 
this one actually fails 
read from console: this one actually fails 
failed sending a message to network: write tcp 127.0.0.1:51194: broken pipe 

クライアントは、次のようになります。

package main 

import (
    "net" 
    "os" 
    "io" 
    //"bufio" 
    //"fmt" 
) 

func cp(dst io.Writer, src io.Reader, errc chan<- error) { 

    // -reads from src and writes to dst 
    // -blocks until EOF 
    // -EOF is not an error 
    _, err := io.Copy(dst, src) 

    // push err to the channel when io.Copy returns 
    errc <- err 
} 

func StartCommunication(conn net.Conn) { 

    //create a channel for errors 
    errc := make(chan error) 

    //read connection and print to console 
    go cp(os.Stdout, conn, errc) 

    //read user input and write to connection 
    go cp(conn, os.Stdin, errc) 

    //wait until nil or an error arrives 
    err := <- errc 

    if err != nil { 
     println("cp error: ", err.Error()) 
    } 
} 

func main() { 

    servAddr := "localhost:6666" 

    tcpAddr, err := net.ResolveTCPAddr("tcp", servAddr) 

    if err != nil { 
     println("ResolveTCPAddr failed:", err.Error()) 
     os.Exit(1) 
    } 

    conn, err := net.DialTCP("tcp", nil, tcpAddr) 

    if err != nil { 
     println("net.DialTCP failed:", err.Error()) 
     os.Exit(1) 
    } 

    defer conn.Close() 

    StartCommunication(conn) 

} 

EDIT:JimBの提案Iに続き実際の例を思いついた。メッセージはもはや失われず、新しい接続で再送信されます。私はかなり安全ではありますが、別の実行ルーチン間で共有変数(connWrap.IsFaulted)を使用することはどれほど安全ですか。

+1

答えを含めるように質問を編集するのではなく、あなた自身の質問に回答することをお勧めします。 – Joakim

答えて

9

これはGo固有のものではなく、通過するTCPソケットの成果物です。

TCP終了ステップのまともな図は、このページの下部にある: http://www.tcpipguide.com/free/t_TCPConnectionTermination-2.htm

簡易版は、クライアントがそのソケットを閉じたときに、それはFINを送信し、サーバーからのACKを受信することです。その後、サーバーが同じ処理を行うのを待ちます。しかし、FINを送信する代わりに、より多くのデータを送信しています。これは破棄されます。クライアントソケットは、あなたから送信されたデータが無効であるとみなし、次回RSTを送信すると、表示されるエラーに置き換えてください。

プログラムに戻って、何とかこれを処理する必要があります。一般的に、送信を開始する担当者は誰でも考えることができ、また終了の開始を担当しているので、サーバはまでの送信を続けることができると仮定してください。は接続を終了するか、エラーに遭遇します。クライアントのクローズをより確実に検出する必要がある場合は、プロトコルで何らかのクライアントレスポンスが必要です。こうすることで、recvをソケット上で呼び出して0を返すことができます。これにより閉じられた接続が通知されます。

これは、接続のReadメソッド(または、あなたのケースのコピー内)からEOFエラーを返します。小さな書き込みが行なわれて静かにドロップされるため、SetWriteDeadlineは機能しません。あるいは、クライアントは最終的にRSTで応答し、エラーを返します。

+0

ありがとう、私は今実例があります。 –

関連する問題