设计要求:在课本第15章Java网络通信例15.3、15.4的基础上,编写完成以下功能的小型Java聊天室系统。
- 多客户端模式下,实现客户与客户的单独通信,要求信息通过服务器中转。
- 端到端的通信,实现并行模式
- 实现端到端的文件传输。
多客户的单独通信
- 改进思路:在课本例15.3、15.5的基础上,实现多客户的情况下实现客户与客户之间的通信,关键在于服务器端与客户端连接时,获取与客户端一一对应的socket套接字需要在服务器端中存储起来,在客户端收或者发信息时,只要在服务端找到对应的socket即可建立连接,互相传输信息,从而使服务器在聊天过程中起到中转消息的功能。
- 我采用的方法是创建一个ArrayList类的对象SocketRecord,将服务器获取的socket按建立连接的顺序存储起来,使用其add() 和get() 方法存入或获取相应的socket。
- 过程中出现的问题:如下图所示,如客户0与客户1聊天,客户0先给客户1发送消息,此时客户1并不能及时地收到,此时,客户1若在未知的情况下回客户0一条信息,之前客户0发的消息便会同时出现。以上过程服务器端运行过程正常。
- 原因:readline() 方法引起的阻塞问题。以下是客户端收发消息的程序片段。readline()是一个阻塞函数,当没有数据读取时就一直阻塞在那,只有当数据流关闭或者读取到“/r”“/n”“/r/n”才会返回。所以在收消息的客户端中,最初系统没有输入就会一直阻塞在
if((readline=sin.readline())!=null)
不会继续执行底下收消息的动作,只有在最初本应收消息的客户端也系统输入之后,就不再阻塞,也就可以接收那条最初本应接收的消息。 - 解决办法:将客户端收发消息的操作分为收消息线程与发消息线程,同时实现要求二实现端到端的并行模式。
并行模式
- 改进思路:主要是对客户端收发消息的功能进行改动,客户端在与服务器通过获取对方socket套接字建立连接之后,就创建两个线程分别收发消息。收发消息分两个线程,实现并行模式使得收发消息互不影响。
- 代码如下
package ChatBox;
import java.io.*;
import java.net.*;
import java.util.*;
/*服务器端*/
public class TalkServer {
public static List<Socket> SocketRecord = new ArrayList<Socket>();
// 用ArrayList记录下不同用户的Socket
static int clientnum = 0;
public static void main(String args[]) throws IOException {
ServerSocket serverSocket = null;
boolean listening = true;
try {
serverSocket = new ServerSocket(4700);
} catch (IOException e) {
System.out.println("Could not listen on port:4700.");
System.exit(-1);
}
while (listening) {
SocketRecord.add(clientnum, serverSocket.accept());
// 将接收到的用户保存到ArrayList相应的位置中
new ServerThread(SocketRecord.get(clientnum), clientnum).start();// 启动相应线程
clientnum++;
}
serverSocket.close();
}
}
/*服务器的线程*/
public class ServerThread extends Thread {
Socket socket = null;// 发消息客户端的socket
Socket receive_socket = null;// 接收消息客户端的socket
int Label;// 标记当前客户端编号
int clientnum;
public ServerThread(Socket socket, int num) {
this.socket = socket;
clientnum = num + 1;
Label = num;// Label是从0开始的
}
public void run() {
try {
BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true) {
String line = is.readLine();
String[] talkTo = line.split(" ");
// 输入中用空格把向谁发消息和消息的内容分隔开,分别存入数组中
int number = Integer.parseInt(talkTo[0]);
// 用number记录当前向谁发消息
System.out.print("Client" + Label + " is taking with Client" + number + ": ");
System.out.println(talkTo[1]);
receive_socket = TalkServer.SocketRecord.get(number);
// 获得接收消息的客户端的socket
PrintWriter os = new PrintWriter(receive_socket.getOutputStream());
os.println(Label + " " + talkTo[1]);
// 消息发送给接收的客户端
os.flush();
if (talkTo[1].equals("bye")) {
os.close();
break;
}
}
is.close();
socket.close();
} catch (Exception e) {
System.out.println("Error:" + e);
}
}
}
/*客户端*/
public class TalkClient {
public static void main(String args[]) {
try {
Socket socket = new Socket("127.0.0.1", 4700);
System.out.println(socket);
new Thread(new ReceiveClient(socket.getInputStream())).start();// 收消息的线程
new Thread(new SendClient(socket.getOutputStream())).start();// 发消息的线程
} catch (Exception e) {
System.out.println("Error" + e);
}
}
}
// 客户收消息的线程
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ReceiveClient extends Thread {
InputStream in;
String socketline;
public ReceiveClient(InputStream in) {
this.in = in;
}
public void run() {
try {
BufferedReader is = new BufferedReader(new InputStreamReader(in));
while (true) {
if ((socketline = is.readLine()) != null)// 收消息
{
String[] istalk = socketline.split(" ");
System.out.println(istalk[0] + ": " + istalk[1]);
}
if (socketline.equals("bye") || socketline == null)
break;
}
is.close();
} catch (Exception e) {
System.out.println("Error" + e);
}
}
}
//客户发消息的线程
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
public class SendClient extends Thread {
OutputStream out;
String[] mytalk;
String readline;
public SendClient(OutputStream out) {
this.out = out;
}
public void run() {
try {
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
// 系统输入
PrintWriter os = new PrintWriter(out);
// 由Socket对象得到输出
System.out.println("Who and What? Like'0 xxxxx.'");
String[] mytalk;
while (true) {
if ((readline = sin.readLine()) != null)// 系统输入
{
mytalk = readline.split(" ");
os.println(readline);
os.flush();
System.out.println("me: " + mytalk[1]);
}
if (readline.equals("bye") || readline == null)
break;// 系统输入为bye则结束对话
}
os.close();
} catch (Exception e) {
System.out.println("Error" + e);
}
}
}
运行结果如下:
- 服务器端
- 客户端0
- 客户端1
- 客户端2
关于文件传输的在下篇更~
转载自原文链接, 如需删除请联系管理员。
原文链接:Java:小型聊天室系统 个人设计分析总结(一),转载请注明来源!