Java的String构造方法的异同?

在实现分布式作业时,遇到Java UDP通信中使用不同String构造方法导致的收发异常。原本代码中使用`new String(receiveBuffer)`时,客户端仅能接收第一行消息,而改为`new String(datagram.getData(),0,datagram.getLength())`后恢复正常。问题原因可能与字符串创建和数据包解析有关,目前尚不清楚具体差异,期待进一步的解答。附带了修改后的关键代码段。" 123220954,9178144,使用Docker迁移百度云资源至Adrive,"['云存储', '数据迁移', 'Docker应用', '百度云盘', 'Adrive']
摘要由CSDN通过智能技术生成

这两天做分布式作业,改书上的源代码,想实现每个客户想服务器发送一个名称,服务器累加从连续客户接收到的名字(后面加一个换行符\n,并添加到一个静态字符串)。服务器收到名字后,将收集到的名字发送给客户这样的协议。

奇怪的是,按照书上原本的收到的String以new String(receiveBuffer)的时候构造接收的消息的时候,发送和接收会不如预期进行,客户端只能收到第一行的消息,之后的几行都收不到了。而改成用new String(datagram.getData(),0,datagram.getLength())的时候,收发正常。我感觉好奇怪,网上搜这两个方法的差异,也不知道如何解释。先记录在此。若有人看到有想法的,请多多指教。谢谢。


顺便把作业贴出来自留。

修改的关键代码为

static StringBuffer sb=new StringBuffer();
if(sb.length() == 0)
             sb.append(message);
             else
             sb.append("\n" + message);

i)无连接服务器和客户

客户端UML图,其中Client1属于表示层,ClientHelper1属于应用逻辑层,MyClientDatagramSocket属于服务逻辑层。

 

服务器端UML图,EchoServer1属于表示+应用逻辑层,MyServerDatagramSocket属于服务逻辑层。

 

 

程序清单:

客户端:

表示层:

package chapter5;
 
import java.io.*;
 
/**
 * This module contains the presentaton logic of an Echo Client.
 * @author M. L. Liu
 */
public class EchoClient1 {
   static final String endMessage = ".";
   public static void main(String[] args) {
      InputStreamReader is = new InputStreamReader(System.in);
      BufferedReader br = new BufferedReader(is);
      try {
         System.out.println("Welcome to the Echo client.\n" +
                            "What is the name of the server host?");
         String hostName = br.readLine();
         if (hostName.length() == 0) // if user did not enter a name
            hostName = "localhost";  //   use the default host name
         System.out.println("What is the port number of the server host?");
         String portNum = br.readLine();
         if (portNum.length() == 0)
            portNum = "7";          // default port number
         EchoClientHelper1 helper = 
            new EchoClientHelper1(hostName, portNum);
         boolean done = false;
         String message, echo;
         while (!done) {
            System.out.println("Enter a line to receive an echo back from the server, "
                            + "or a single peroid to quit.");
            message = br.readLine( );
            if ((message.trim()).equals (endMessage)){
               done = true;
               helper.done( );
            }
            else {
               echo = helper.getEcho( message);
               System.out.println(echo);
            }
          } // end while
      } // end try  
      catch (Exception ex) {
         ex.printStackTrace( );
      } // end catch
   } //end main
} // end class      
 

应用逻辑层:

package chapter5;
import java.net.*;
import java.io.*;
 
/**
 * This class is a module which provides the application logic
 * for an Echo client using connectionless datagram socket.
 * @author M. L. Liu
 */
public class EchoClientHelper1 {
   private MyClientDatagramSocket mySocket;
   private InetAddress serverHost;
   private int serverPort;
 
   EchoClientHelper1(String hostName, String portNum) 
      throws SocketException, UnknownHostException { 
  	   this.serverHost = InetAddress.getByName(hostName);
  	 this.serverPort = Integer.parseInt(portNum);
      // instantiates a datagram socket for both sending
      // and receiving data
   	this.mySocket = new MyClientDatagramSocket(); 
   } 
   public String getEcho( String message) 
      throws SocketException, IOException {                                                                                 
      String echo = "";    
      mySocket.sendMessage( serverHost, serverPort, message);
   // now receive the echo
      echo = mySocket.receiveMessage();
      return echo;
   } //end getEcho
 
   public void done( ) throws SocketException {
      mySocket.close( );
   }  //end done
 
} //end class<span style="color: rgb(63, 127, 95); font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>


服务逻辑层:

package chapter5;
import java.net.*;
import java.io.*;
 
/**
 * A subclass of DatagramSocket which contains 
 * methods for sending and receiving messages
 * @author M. L. Liu
 */
public class MyClientDatagramSocket extends DatagramSocket {
static final int MAX_LEN = 100;  
   MyClientDatagramSocket( ) throws SocketException{
     super( );
   }
   MyClientDatagramSocket(int portNo) throws SocketException{
     super(portNo);
   }
   public void sendMessage(InetAddress receiverHost,
                           int receiverPort,
                           String message)
   	           throws IOException {
         byte[ ] sendBuffer = message.getBytes( );      
         DatagramPacket datagram = 
            new DatagramPacket(sendBuffer, sendBuffer.length, 
                                  receiverHost, receiverPort);
         this.send(datagram);
   } // end sendMessage
 
   public String receiveMessage()
throws IOException {
         byte[ ] receiveBuffer = new byte[MAX_LEN];
         DatagramPacket datagram =
            new DatagramPacket(receiveBuffer, MAX_LEN);
         this.receive(datagram);
        // String message = new String(receiveBuffer);
         String message=new String(datagram.getData(),0,datagram.getLength());
         return message;
   } //end receiveMessage
} //end class

服务器端:

应用逻辑层:

package chapter5;
import java.io.*;
 
/**
 * This module contains the application logic of an echo server
 * which uses a connectionless datagram socket for interprocess 
 * communication.
 * A command-line argument is required to specify the server port.
 * @author M. L. Liu
 */
 
public class EchoServer1 {
   public static void main(String[] args) {
  StringBuffer sb=new StringBuffer();
      int serverPort = 7;    // default port
      if (args.length == 1 )
         serverPort = Integer.parseInt(args[0]);       
      try {
         // instantiates a datagram socket for both sending
         // and receiving data
   	   MyServerDatagramSocket mySocket = new MyServerDatagramSocket(serverPort); 
         System.out.println("Echo server ready.");  
         while (true) {  // forever loop
            DatagramMessage request = 
               mySocket.receiveMessageAndSender();
            System.out.println("Request received");
            String message = request.getMessage( );
            //维护全局状态信息
            if(sb.length()==0)
            	sb.append(message);
            else sb.append('\n'+message);
            System.out.println("message received: \n"+ message);
            System.out.println("static string now:\n"+sb.toString());
            // Now send the echo to the requestor
            mySocket.sendMessage(request.getAddress( ),
               request.getPort( ),sb.toString());
   } //end while
       } // end try
    catch (Exception ex) {
          ex.printStackTrace( );
    } // end catch
   } //end main
} // end class   

服务逻辑层:

package chapter5;
import java.net.*;
import java.io.*;
 
/**
 * A subclass of DatagramSocket which contains 
 * methods for sending and receiving messages
 * @author M. L. Liu
 */
 
public class MyServerDatagramSocket extends DatagramSocket {
static final int MAX_LEN = 100; 
   MyServerDatagramSocket(int portNo) throws SocketException{
     super(portNo);
   }
   public void sendMessage(InetAddress receiverHost,
                           int receiverPort,
                           String message)
   	           throws IOException {
         byte[ ] sendBuffer = message.getBytes();                                     
         DatagramPacket datagram = 
            new DatagramPacket(sendBuffer, sendBuffer.length, 
                                  receiverHost, receiverPort);
         this.send(datagram);
   } // end sendMessage
 
   public String receiveMessage( )
throws IOException {
         byte[ ] receiveBuffer = new byte[MAX_LEN];
         DatagramPacket datagram =
            new DatagramPacket(receiveBuffer, MAX_LEN);
         this.receive(datagram);
         //String message = new String(receiveBuffer);
         String message=new String(datagram.getData(),0,datagram.getLength());
         return message;
   } //end receiveMessage
 
   public DatagramMessage receiveMessageAndSender( )
throws IOException {
         byte[ ] receiveBuffer = new byte[MAX_LEN];
         DatagramPacket datagram =
            new DatagramPacket(receiveBuffer, MAX_LEN);
         this.receive(datagram);
         // create a DatagramMessage object, to contain message
         //   received and sender's address
         DatagramMessage returnVal = new DatagramMessage( );
         returnVal.putVal(new String(datagram.getData(),0,datagram.getLength()),
                          datagram.getAddress( ),
                          datagram.getPort( ));
         return returnVal;
   } //end receiveMessage
} //end class

数据报:

package chapter5;
import java.net.*;
/**
 * A class to use with MyServerDatagramSocket for
 * returning a message and the sender's address
 * @author M. L. Liu
 */
public class DatagramMessage{
   private String message;
   private InetAddress senderAddress;
   private int senderPort;
   public void putVal(String message, InetAddress addr, int port) {
      this.message = message;
      this.senderAddress = addr;
      this.senderPort = port;
   }
 
   public String getMessage( ) {
      return this.message;
   }
 
   public InetAddress getAddress( ) {
      return this.senderAddress;
   }
 
   public int getPort( ) {
      return this.senderPort;
   }
} // end class  

ii)面向连接的迭代服务器和客户

为方便处理,服务器端发送的时候将字符串中的\n换成\\n,客户端收到的时候再换过来。

 

客户端UML图,EchoClient2属于表示层,EchoClientHelper2属于应用逻辑层,MyStreamSocket属于服务逻辑层。

 

服务器端UML图,EchoServer2属于表示逻辑和应用逻辑层,其他属于服务逻辑层。

 


代码清单:

客户端:

表示层:

package chapter5;
import java.io.*;
 
/**
 * This module contains the presentaton logic of an Echo Client.
 * @author M. L. Liu
 */
public class EchoClient2 {
   static final String endMessage = ".";
   public static void main(String[] args) {
      InputStreamReader is = new InputStreamReader(System.in);
      BufferedReader br = new BufferedReader(is);
      try {
         System.out.println("Welcome to the Echo client.\n" +
            "What is the name of the server host?");
         String hostName = br.readLine();
         if (hostName.length() == 0) // if user did not enter a name
            hostName = "localhost";  //   use the default host name
         System.out.println("What is the port number of the server host?");
         String portNum = br.readLine();
         if (portNum.length() == 0)
            portNum = "7";          // default port number
         EchoClientHelper2 helper = 
            new EchoClientHelper2(hostName, portNum);
         boolean done = false;
         String message, echo;
         while (!done) {
            System.out.println("Enter a line to receive an echo "
               + "from the server, or a single period to quit.");
            message = br.readLine( );
            if ((message.trim()).equals (endMessage)){
               done = true;
               helper.done( );
            }
            else {
            	//注意字符串的处理
               echo = helper.getEcho( message).replace("\\n", "\n");
               System.out.println(echo);
            }
          } // end while
      } // end try  
      catch (Exception ex) {
         ex.printStackTrace( );
      } //end catch
   } //end main
} // end class

应用逻辑层:

package chapter5;
import java.net.*;
import java.io.*;
 
/**
 * This class is a module which provides the application logic
 * for an Echo client using stream-mode socket.
 * @author M. L. Liu
 */
 
public class EchoClientHelper2 {
 
   static final String endMessage = ".";
   private MyStreamSocket mySocket;
   private InetAddress serverHost;
   private int serverPort;
 
   EchoClientHelper2(String hostName,
                     String portNum) throws SocketException,
                     UnknownHostException, IOException {
                                     
  	   this.serverHost = InetAddress.getByName(hostName);
  	 this.serverPort = Integer.parseInt(portNum);
      //Instantiates a stream-mode socket and wait for a connection.
   	this.mySocket = new MyStreamSocket(this.serverHost,
         this.serverPort); 
/**/  System.out.println("Connection request made");
   } // end constructor
   public String getEcho( String message) throws SocketException,
      IOException{     
      String echo = "";    
      mySocket.sendMessage( message);
   // now receive the echo
      echo = mySocket.receiveMessage();
      return echo;
   } // end getEcho
 
   public void done( ) throws SocketException,
                              IOException{
      mySocket.sendMessage(endMessage);
      mySocket.close( );
   } // end done 
} //end class

服务逻辑层:

package chapter5;
import java.net.*;
import java.io.*;
 
/**
 * A wrapper class of Socket which contains 
 * methods for sending and receiving messages
 * @author M. L. Liu
 */
public class MyStreamSocket extends Socket {
   private Socket  socket;
   private BufferedReader input;
   private PrintWriter output;
 
   MyStreamSocket(InetAddress acceptorHost,
                  int acceptorPort ) throws SocketException,
                                   IOException{
      socket = new Socket(acceptorHost, acceptorPort );
      setStreams( );
 
   }
 
   MyStreamSocket(Socket socket)  throws IOException {
      this.socket = socket;
      setStreams( );
   }
 
   private void setStreams( ) throws IOException{
      // get an input stream for reading from the data socket
      InputStream inStream = socket.getInputStream();
      input = 
         new BufferedReader(new InputStreamReader(inStream));
      OutputStream outStream = socket.getOutputStream();
      // create a PrinterWriter object for character-mode output
      output = 
         new PrintWriter(new OutputStreamWriter(outStream));
   }
 
   public void sendMessage(String message)
   	           throws IOException {
      output.println(message);   
      //The ensuing flush method call is necessary for the data to
      // be written to the socket data stream before the
      // socket is closed.
      output.flush();               
   } // end sendMessage
 
   public String receiveMessage( )
throws IOException {
      // read a line from the data stream
      String message = input.readLine( );  
      return message;
   } //end receiveMessage
 
} //end class

服务器:

应用逻辑层:

package chapter5;
import java.io.*;
import java.net.*;
 
/**
 * This module contains the application logic of an echo server
 * which uses a stream socket for interprocess  communication.
 * A command-line argument is required to specify the server port.
 * @author M. L. Liu
 */
public class EchoServer2 {
   static final String endMessage = ".";
   private static StringBuffer sbf = new StringBuffer();
   public static void main(String[] args) {
      int serverPort = 7;    // default port
      String message;
 
      if (args.length == 1 )
         serverPort = Integer.parseInt(args[0]);       
      try {
         // instantiates a stream socket for accepting
         //   connections
   	   ServerSocket myConnectionSocket = 
            new ServerSocket(serverPort); 
/**/     System.out.println("server ready.");  
         while (true) {  // forever loop
            // wait to accept a connection 
/**/        System.out.println("Waiting for a connection.");
            MyStreamSocket myDataSocket = new MyStreamSocket
                (myConnectionSocket.accept( ));
/**/        System.out.println("connection accepted");
            boolean done = false;
            while (!done) {
               message = myDataSocket.receiveMessage( );
/**/           System.out.println("message received: "+ message);
               //维护全局信息
               if(sbf.length() == 0)
               sbf.append(message);
               else
               sbf.append("\n" + message);
               if ((message.trim()).equals (endMessage)){
                  //Session over; close the data socket.
/**/              System.out.println("Session over.");
                  myDataSocket.close( );
                  done = true;
               } //end if
               else {
                  // Now send the echo to the requestor
            	   //注意字符串的处理
                  myDataSocket.sendMessage(sbf.toString().replace("\n", "\\n"));
               } //end else
       } //end while !done
         } //end while forever
       } // end try
    catch (Exception ex) {
          ex.printStackTrace( );
    }
   } //end main
} // end class

服务逻辑层:

与客户端的服务逻辑层相同。

 

iii)面向连接的并发服务器和客户

客户端UML图同ii

服务器端UML图,EchoServer3属于表示层+应用逻辑层,其他属于服务逻辑层。


使用并发技术。

代码清单:

客户端与ii中的一样。见上。

 

服务器端:

应用逻辑层:

package chapter5;
import java.io.*;
import java.net.*;
 
/**
 * This module contains the application logic of an echo server
 * which uses a stream-mode socket for interprocess communication.
 * Unlike EchoServer2, this server services clients concurrently.
 * A command-line argument is required to specify the server port.
 * @author M. L. Liu
 */
 
public class EchoServer3 {
   public static void main(String[] args) {
      int serverPort = 7;    // default port
      String message;
 
      if (args.length == 1 )
         serverPort = Integer.parseInt(args[0]);       
      try {
         // instantiates a stream socket for accepting
         //   connections
   	   ServerSocket myConnectionSocket = 
            new ServerSocket(serverPort); 
/**/     System.out.println("Echo server ready.");  
         while (true) {  // forever loop
            // wait to accept a connection 
/**/        System.out.println("Waiting for a connection.");
            MyStreamSocket myDataSocket = new MyStreamSocket
                (myConnectionSocket.accept( ));
/**/        System.out.println("connection accepted");
            // Start a thread to handle this client's sesson
            Thread theThread = 
               new Thread(new EchoServerThread(myDataSocket));
            theThread.start();
            // and go on to the next client
            } //end while forever
       } // end try
    catch (Exception ex) {
          ex.printStackTrace( );
    } // end catch
   } //end main
} // end class

package chapter5;
import java.io.*;
/**
 * This module is to be used with a concurrent Echo server.
 * Its run method carries out the logic of a client session.
 * @author M. L. Liu
 */
 
class EchoServerThread implements Runnable {
   static final String endMessage = ".";
   static StringBuffer sb=new StringBuffer();
   MyStreamSocket myDataSocket;
 
   EchoServerThread(MyStreamSocket myDataSocket) {
      this.myDataSocket = myDataSocket;
   }
 
   public void run( ) {
      boolean done = false;
      String message;
      try {
         while (!done) {
             message = myDataSocket.receiveMessage( );
/**/         System.out.println("message received: "+ message);
             if(sb.length() == 0)
             sb.append(message);
             else
             sb.append("\n" + message);
             if ((message.trim()).equals (endMessage)){
                //Session over; close the data socket.
/**/            System.out.println("Session over.");
                myDataSocket.close( );
                done = true;
             } //end if
             else {
                // Now send the echo to the requestor
                //注意处理字符串
                myDataSocket.sendMessage(sb.toString().replace("\n", "\\n"));
             } //end else
          } //end while !done
        }// end try
        catch (Exception ex) {
           System.out.println("Exception caught in thread: " + ex);
        } // end catch
   } //end run
} //end class 

服务逻辑层:

ii,见上。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值