2016-01-19 42 views
1

這是an earlier question的延續。我開始一個新線程,以便我可以顯示最新的完整服務器和客戶端代碼,包括我在該線程和Reddit上收到的所有建議。爲什麼我不能使用TCP客戶端發送兩條消息? (續)

我在Windows 7 Pro下運行。

我有一個用Python編寫的非常簡單的TCP服務器。它創建一個socketserver對象並等待消息。當到達時,服務器將其打印到其控制檯並通過相同的端口發回簡單的確認。

客戶端向服務器發送編號消息,等待確認並顯示它。然後它會詢問用戶是否應該發送另一條消息。

第一封郵件已成功發送並確認。在客戶端,看起來第二條消息發送成功;對網絡流的Write()方法的調用成功。但是,當調用Read()消息來獲取確認時,會拋出異常:「建立的連接被主機中的軟件中止。」

這裏是服務器代碼:

import json 
import threading 

import socketserver 
import time 

with open('CAPS_TWMS_config.json', 'rt') as c: 
    caps_config = json.load(c) 

# We are listening on this port and all defined IP addresses 
# listenPort = 5001 
listenPort = caps_config["listen_port"] 

# Were to send the information to. 
clientIPAddress = '127.0.0.1' # socket.gethostbyname('client') 
# clientPort = 12345 
clientPort = caps_config["send_port"] 

dsnName = caps_config["dsn_name"] 

# Message sequence number 
sequence_num = 1 
exit_app = False 

class ListenSocketHandler(socketserver.BaseRequestHandler): 
    """ 
    The RequestHandler class for our server. 

    It is instantiated once per connection to the server, and must 
    override the handle() method to implement communication to the 
    client. 
    """ 
    def __init__(self, request, client_address, this_server): 
     socketserver.BaseRequestHandler.__init__(self, request, client_address, this_server) 
     self.timeout = 10 

    def handle(self): 
     try: 
      data = self.request.recv(1024).decode() 
      # print (str.format("dataString[21]: {0}; dataString[24:26]: {1}", data[21], data[24:26])) 
      print ('ListenSocketHandler recv()-> "%s"' % data) 
      print ('ListenSocketHandler recv().length-> "%d"' % len(data)) 

      if len(data) > 0: 
       self.request.send("I got a message!".encode()) 
       return 
     except Exception as value: 
      print('ListenSocketHandler - %s' % str(value)) 

     return 


class ListenServer(socketserver.ThreadingMixIn, socketserver.TCPServer): 
    """ 
    The multi-threaded server that will spawn threads running the Socket Handler for each 
    connection 
    """ 
    pass 

if __name__ == '__main__': 

    try: 
     # Create the Server Handler for connections to this computer listening on all IP addresses, 
     # change '' to 'x.x.x.x' to listen on a specific IP network. This class will listen for messages # from CAPS. 
     server = ListenServer(('', listenPort), ListenSocketHandler) 
     ip, port = server.server_address 

     # Start a thread with the server -- that thread will then start one 
     # more thread for each request 
     server_thread = threading.Thread(target=server.serve_forever) 
     # Exit the server thread when the main thread terminates 
     server_thread.setDaemon(True) 
     server_thread.start() 

     while not exit_app: 
      time.sleep(1) 

     print ('Out of main loop.') 
     server.shutdown() 
     server.server_close() 
    except Exception as value: 
     print("Failed to do something: %s", str(value)) 

下面是客戶端代碼:

private void button1_Click(object sender, EventArgs e) 
{ 
    TcpClient client = new TcpClient(); 

    try 
    { 
     client.Connect("127.0.0.1", 5001); 
     NetworkStream stream = client.GetStream(); 
     int messageCount = 1; 

     while (true) 
     {      
      // Translate the passed message into ASCII and store it as a Byte array. 
      string message = string.Format("This is message {0}", messageCount++); 
      Byte[] data = System.Text.Encoding.ASCII.GetBytes(message); 


      // Send the message to the connected TcpServer. 
      stream.Write(data, 0, data.Length); 

      // Receive the TcpServer.response. 

      // Buffer to store the response bytes. 
      data = new Byte[1024]; 

      // String to store the response ASCII representation. 
      String responseData = String.Empty; 

      // Read the first batch of the TcpServer response bytes. 
      Int32 receivedCount = 0; 
      int sleepCount = 0; 
      while (receivedCount == 0) 
      {       
       receivedCount = stream.Read(data, 0, data.Length); 
       responseData = System.Text.Encoding.ASCII.GetString(data, 0, receivedCount); 
       if (receivedCount == 0) 
       { 
        Thread.Sleep(250); 
        if (sleepCount++ > 20) 
        { 
         MessageBox.Show("Response timeout."); 
         break; 
        } 
       } 
      } 

      if (MessageBox.Show("Reply: " + responseData + " Try again?", "Try again?", MessageBoxButtons.YesNo) == DialogResult.No) 
      { 
       break; 
      } 

     } 
     client.Close(); 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show("Failed to do something with TcpClient: " + ex.Message); 
    } 
} 
+0

您如何期待服務器知道一個請求的結束和下一個請求的開始? –

+0

此外,在從處理程序返回之前,請嘗試'self.request.shutdown(socket.SHUT_RDWR)' –

+0

檢查以查看是否要訪問:client.Close();. TCP在消息中使用零字節獲取數據是正常的,因此接收計數可能爲零,您將退出while循環。您需要接收,直到收到所有數據。因此,使用3種方法中的1種:1)Ascii:接收,直到接收到已知字符,如'\ n'2)Ascii或Binary:將字節數添加到消息開頭,並讀取直到接收到所有字節。 3)ASCII或二進制:固定長度的消息。每個消息(或消息類型)具有已知數量的字符。 – jdweng

回答

0

在你的代碼,每當一個新的客戶端連接它讀取數據並確認時間客戶和處理程序在以下語句後立即關閉(即返回):

self.request.send("I got a message!".encode()) 
       return 

Henc e連接被關閉,並且來自客戶端的進一步命令不發送。你可以等待一個循環來接收和確認。

+0

我不相信這是正確的。當創建socketserver對象時,會指定ListenSocketHandler類來處理傳入數據。收到消息時,將調用ListenSocketHandler類的handle()方法。該方法處理消息(通過發送ack)並返回。該方法不是設計爲永遠在循環中運行,並且從該方法返回並不會終止連接。 –

+0

哦..是這樣嗎?我的理解是默認連接處理程序(ListenSocketHandler)除非在handle()方法中實現某些內容,否則不會執行任何操作。所以你的handle()將被調用,每一個從客戶端連接的新連接都不會被客戶端的每條消息所調用。這裏是簡單的回聲tcp服務器[鏈接](http://docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-19-sect-2.html#pythonian-CHP-19-EX-5)。 – Venkatesh

+0

你通過添加循環檢查了嗎? – Venkatesh

相關問題