这两天做分布式作业,改书上的源代码,想实现每个客户想服务器发送一个名称,服务器累加从连续客户接收到的名字(后面加一个换行符\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,见上。