Practical Quiz 1 of SSD 8 -- JAVA
——花了三天时间,终于写完了。
[题目要求]以给定聊天室客户端,要求完成与其相匹配的服务器后台程序。
[相关资源]聊天室客户端GUIClient.jar
[关键技术]首先是要实现ServerSocket的多线程操作;其次,客户端的输入输出流的格式和规范要把握准确;再次,对于广播功能的实现也非常重要,这涉及到了多个线程之间的联系问题。搞定这三点,基本;离成功就不远了。
[心得体会]
1.对输入输出流类型和具体方法的判定:这可是搞了N久才发现的问题啊~ 本来以为什么流都可以,所以总是以为别的地方出了问题。本人的做法是把流类型定为大小通吃的DataInputStream和DataOutputStream,把所有读写String的方式挨个试了一遍,其实工作量不大,就3种。时发现.writeUTF(String)和.readUTF()可以做到;
2.对于多线程的相关技术,本人采用的是继承java.net.Thread类的思路,并重载run()函数。而另外,在整个ChatServer类里定义一个静态Vector变量,以容纳广播时需要用到的输出流集合。
3.细枝末节,大框架打好之后,可以对各个功能进行细节上的修改。比如对各类命令可用性的判定,还有对在聊天时使关键字"^"等情况的处理,对重名登陆用户的处理,对服务器控制台的利用等等,本人都作了比较独特的设计在里面。
本着学术交流的目的和宗旨,欢迎大家的七嘴八舌~
iCarnegie-SSD8-PQ1原题如下:
[Ctrl+A 全部选择 提示:你可先修改部分代码,再按运行]
本人编写的程序ReadMe文件如下:
引用:
Readme for Practical Quiz 1 of SSD 8
Here are the instructions on compiling and running each program:
ChatServer.java
First, compile this java file through cmd:
javac ChatServer.java
to ChatServer.class.
Second, run this class file also through cmd:
java ChatServer <port>
Now the chat server is running. This chat server will accept all the GUIClient's connections and broadcast their statements and messages. At the same time, the broadcast contents will be displayed on the chat server's console window just as same as the GUIClient's message format. The chat server manager can look out them very quickly and conveniently.
Note:
1. If no port number input, the chat server wouldn't run.
2. If the GUIClient send an unacceptable request to the chat server, the program will input an error message on the chat server's console window.
3. If the GUIClient send a message which has character "^" in the middle, the chat server will change them into blanks automatically.
4. If the same nickname appears, the chat server will make the nicknames different automatically.
GUIClient.jar
When a chat server has run, you can open another cmd window and run the given client program like this:
Java –jar GUIClient.jar <hostname> <port> <nickname>
Then the connection between client and chat server will be built. And you can chat with each other clients through a GUI window.
When someone joins in, the chat box will display:
Server: <nickname> has joined from <client name> (<current time>)
When someone part out, the chat box will display:
Server: <nickname> has parted out who is at <client name> (<current time>)
When someone sends message, the chat box will display:
<nickname> : <message>
Note:
1. If you don't input the hostname, port number or your nickname with correct formation to the GUIClient, the program will input an error message on the screen.
2. When chatting, you can input "^" in the sentences. But the other GUIClients will not be able to receive it.
本人编写的服务端程序代码如下:
FileName: ChatServer.java
import java.io.*;
import java.net.*;
import java.util.*;
public class ChatServer {
/* the counter of the connection */
static int counter = 1;
/* the host address */
static String serverAddress = "";
/* the group of the DataOutputStream for broadcasting */
static Vector<String> vectorAlias = new Vector<String>();
/* the group of the DataOutputStream for broadcasting */
static Vector<DataOutputStream> vectorStream = new Vector<DataOutputStream>();
/* error type and the information */
final String ERROR_ONE = "Error: Incorrect NUMBER OF TOKENS!";
final String ERROR_TWO = "Error: Incorrect TYPE LENGTH!";
final String ERROR_THREE = "Error: Incorrect HOST DESCRIPTION!";
final String ERROR_FOUR = "Error: Incorrect TYPE VALUE!";
/**
* Create a server socket as a chat server and waiting for connections and
* requests from clients.
*
* @throws Exception
*/
public ChatServer(int port) throws Exception {
/* the server socket */
ServerSocket sk = new ServerSocket(port);
/* the host address */
serverAddress = InetAddress.getLocalHost().getHostAddress();
System.out.println("Welcome to running chat server! (adderss: "
+ serverAddress + " port: " + port + ")\n");
while (true) {
Socket cs = sk.accept();
System.out.println("Accepting Client" + counter
+ "'s connection from: " + serverAddress);
/* a thread for a client */
ServerThread st = new ServerThread(cs, counter++);
st.start();
}
}
class ServerThread extends Thread {
/* a client socket and the counter */
private Socket cs;
private int counter;
public ServerThread(Socket cs, int counter) {
this.counter = counter;
this.cs = cs;
}
public void run() {
String request = "";
String type = "";
String host = "";
String alias = "";
String hostip = "";
String hostname = "";
String message = "";
/* reader and writer declarations */
DataInputStream inFromClient = null;
DataOutputStream outToClient = null;
try {
/* reader and writer definations */
inFromClient = new DataInputStream(cs.getInputStream());
outToClient = new DataOutputStream(cs.getOutputStream());
/* push this writer into the broadcasting gruop */
vectorStream.add(outToClient);
/* read the requests and do the actions */
while (true) {
request = inFromClient.readUTF();
/* ERROR 1 prevention */
StringTokenizer token = new StringTokenizer(request, "^");
if (token.countTokens() >= 3) {
type = token.nextToken();
host = token.nextToken();
token.nextToken();
message = "";
while (token.hasMoreTokens()) {
message = message + token.nextToken() + " ";
}
} else {
displayError(ERROR_ONE, outToClient);
continue;
}
/* ERROR 2 prevention */
if (type.length() != 1) {
displayError(ERROR_TWO, outToClient);
continue;
}
/* ERROR 3 prevention */
if (host.contains("@")) {
alias = host.substring(0, host.indexOf("@"));
hostip = host.substring(host.indexOf("@") + 1, host
.length());
} else {
displayError(ERROR_THREE, outToClient);
continue;
}
hostname = InetAddress.getByName(hostip).getHostName();
/* do the actions */
switch (type.charAt(0)) {
case 'm': {
if (message != "")
sendMessage(host, alias, message);
break;
}
case 'j': {
while (vectorAlias.contains(alias)) {
alias = alias + " (" + counter + ")";
}
vectorAlias.add(alias);
joinIn(alias, hostname);
break;
}
case 'p': {
vectorStream.remove(outToClient);
vectorAlias.remove(alias);
partAway(alias, hostname);
break;
}
/* ERROR 4 prevention */
default: {
displayError(ERROR_FOUR, outToClient);
break;
}
}
}
} catch (Exception e) {
} finally {
/*
* Close the reader, writer and client socket.
*
*/
System.out.println("Closing Client " + counter
+ "\'s Connection...\n");
try {
if (inFromClient != null)
inFromClient.close();
if (outToClient != null)
outToClient.close();
if (cs != null)
cs.close();
} catch (Exception e) {
}
}
}
public void displayError(String error, DataOutputStream outToClient)
throws Exception {
System.err.println(error);
outToClient.writeUTF("m^Server@" + serverAddress + "^-^" + error
+ "^");
}
/**
* Broadcst the message sent by a client.
*
* @param host
* @param alias
* @param message
* @throws Exception
*/
public void sendMessage(String host, String alias, String message)
throws Exception {
String bout = "m^" + host + "^-^" + message + "^";
System.out.println("\t" + alias + " says: " + message);
broadcast(bout);
}
/**
* Broadcast a client joining in information.
*
* @param alias
* @param hostname
* @throws Exception
*/
public void joinIn(String alias, String hostname) throws Exception {
String bout = "m^Server@" + serverAddress + "^-^" + alias
+ " has joined from " + hostname + " (" + getCurrentTime()
+ ")^";
System.out.println(alias + " has joined in from: " + hostname
+ "\n");
broadcast(bout);
}
/**
* Broadcast a client parting out information.
*
* @param alias
* @param hostname
* @throws Exception
*/
public void partAway(String alias, String hostname) throws Exception {
String bout = "m^Server@" + serverAddress + "^-^" + alias
+ " has parted out who is at " + hostname + " ("
+ getCurrentTime() + ")^";
System.out.println("\n" + alias + " has parted out who is at: "
+ hostname);
broadcast(bout);
}
/**
* Output the messages to each client who has connected with the chat
* server. And print the information on the Console window.
*
* @param message
* @throws Exception
*/
public void broadcast(String message) throws Exception {
DataOutputStream outToBroadcast;
for (Iterator it = vectorStream.iterator(); it.hasNext();) {
outToBroadcast = (DataOutputStream) it.next();
outToBroadcast.writeUTF(message);
outToBroadcast.flush();
}
}
/**
* Get the current time and return it. The time format is "H:M AM/PM".
*
* @return the current time
*/
public String getCurrentTime() {
int hours = new Date().getHours();
int minutes = new Date().getMinutes();
boolean amOrPm = hours <= 12;
return amOrPm ? (hours + ":" + minutes + " AM") : (hours - 12 + ":"
+ minutes + " PM");
}
}
/**
* create a chat server and do all jobs.
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Arguments error:\n\t"
+ "Please input the port number!");
return;
}
try {
ChatServer chatServer = new ChatServer(Integer.parseInt(args[0]));
} catch (NumberFormatException e) {
System.err.println("Arguments error:\n\t"
+ "Please input the port number!");
}
return;
}
}