聊天室系统需求
1、多客户模式下,客户与客户的单独通信,且信息通过服务器。
2、端到端的通信,实现并行通讯,一端的发送不受另一端的影响。
3、端到端的文件传输
4、图形界面
知识点需求
1、Java网络通信
2、多线程
3、IO文件操作
4、JFrame
重点问题
1、如何保存与客户端相连接的socket,如何辨别
用线程保护的CurrentHashMap储存每个socket和name的键值对,需要某个socket的时候用name把其取出
2、如何使并行通讯
由于readLine()和println()会阻塞,那么需要两个线程分别实现A->B和B->A的数据传输,但如果我们使用了GUI,由于gui本身可以一直存在,那么就可以使用 ,用gui界面代替发送消息的线程,创建KeyListenner ,每次回车传输TextArea内的文本。
3、文件传输如何判定
可以每次从键盘输入数据后,判断该字符串是否是文件的路径,判断文件是否存在,然后在传输文件前先向对方传输一个特定的字符串“{file}”,而对方在接受到这个特定的字符串以后创建文件接受数据,在文件传输结束以后再发送文件结束的特定字符串,表示文件传输结束,可以转到对话功能了。
4、加入要开启其他的对话框如何解决
将main方法与对话的线程分开,客户端可以像服务器一样,每次想要创建新的对话,创建线程来实现它,而main方法可以在终端继续等待输入,开启新的对话框。
5、若有人已经退出了,如何删除CurrentHashMap内的键值对
退出的方法有两个,一个是再文本框内直接输入close,还有一个直接关闭界面,这两种方法在关闭线程执行之前,先向服务器发送代表退出含义的字符串,服务器收到后将其移出CurrentHashMap。
6、程序的不足
1、直接关掉途行界面后的异常处理不完善
2、一对客户端的聊天结束退出后,第二个退出的名字无法在CurrentHashMap中去除
3、再已有对话窗口的基础上再接受其他人的连接请求时会出现消息收发混乱
整体程序的运行过程1、运行服务器,服务器等待客户端连接2、打开客户端1 ,连接上服务器(若连接不上则提示并退出程序),按提示输入名称,将名称发送给服务器,等待服务器返回当前在线的人3、服务器收到客户端的连接请求,看CurrentHashMap内有没有已经在线的用户,有则发送名称列表,无则发送请等待信息,把名称和socket放进CurrentHashMap内4、打开客户端2,连接服务器输入名称,服务器返回客户端1的名称,客户端进行选择输入,服务器确定是否存在该用户,不存在则重新提示,若存在,两个客户端同时打开GUI的图形界面,可以并行通讯5、想要传输文件,输入文件的绝对路径,接受方受到的文件放在相对路径下6、关闭客户端输入close,服务器删除CurrentHashMap内的内容,并提示对面的客户端连接已断开
下面贴上代码
package MyChat;
public class Client {
public Client(){
new ClientThread().start();
}
public static void main(String[] args) {
new Client();
}
}
package MyChat;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.*;
public class ClientGUI extends JFrame{
myPanel jPanel = null;
public ClientGUI(BufferedReader reader,PrintWriter writer,String obj){
this.jPanel = new myPanel(reader,writer,obj);
this.setTitle( "MiniChat with"+obj);
this.setSize(700, 500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.getContentPane().add(BorderLayout.CENTER, jPanel);
this.setVisible(true);
//this.addWindowListener(new java.awt.event.WindowAdapter() {
// public void windowClosing(java.awt.event.WindowEvent e) {
// try {
// writer.println("close");
// writer.flush();
// writer.close();
// reader.close();
// System.exit(0);
// }catch (IOException ex){
// ex.printStackTrace();
// }
// }
// });
}
public void receive(String str){
jPanel.receive(str);
}
}
class myPanel extends JPanel {
protected JTextArea text;
protected JTextField myinput;
private PrintWriter writer;
private String obj;
private BufferedReader reader;
public myPanel(BufferedReader reader,PrintWriter writer,String obj){
this.obj= obj;
this.reader = reader;
this.writer = writer;
text = new JTextArea(20, 33);
text.setFont(new Font("标楷体", Font.BOLD, 16));
text.setLineWrap(true);
text.setEnabled(false);
JScrollPane scroller = new JScrollPane(text);
scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
this.add(scroller);
myinput = new JTextField();
myinput.setColumns(33);
myinput.setFont(new Font("标楷体", Font.BOLD, 16));
myinput.addKeyListener(new inputKeyListener());
this.add(myinput);
new InputStreamThread(reader).start();
}
class inputKeyListener implements KeyListener {
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER){
String str = myinput.getText();
if(str!="") {
if(str.equals("close"))
try{
close();}catch (IOException ex){ ex.printStackTrace();}
if(isFile(str))
{
trans(str);
writer.println(str);
writer.flush();
text.append("Me:" + str + "\n");
myinput.setText("");
}
else {
writer.println(str);
writer.flush();
text.append("Me:" + str + "\n");
myinput.setText("");
}
}
}
}
}
public void receive(String str){
text.append(obj+" : "+str +"\n");
}
public class InputStreamThread extends Thread{
BufferedReader reader;
public InputStreamThread(BufferedReader reader){
this.reader = reader;
}
@Override
public void run(){
System.out.println("Start");
try{
while(true) {
String str = reader.readLine();
if(str.equals("${File}")) {
System.out.println("file");
str = reader.readLine();
FileWriter fileWriter = new FileWriter(str);
while(!str.equals("${Finished}")) {
str = reader.readLine();
fileWriter.append(str);
}
System.out.println("finished");
fileWriter.close();
}
else {
receive(str);
if(str.equals("close")) {
writer.println("close");
writer.flush();
close();
receive("object has closed the chat");
try {
sleep(10000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
//只能识别绝对路径
public boolean isFile(String str){
File dir = new File(str);
return dir.exists();
}
public void trans(String str) {
File dir = new File(str);
try {
FileReader fileReader = new FileReader(dir);
BufferedReader bufferedReader = new BufferedReader(fileReader);
writer.println("${File}");
writer.flush();
writer.println(dir.getName());
writer.flush();
String line = "";
while((line = bufferedReader.readLine())!=null){
writer.println(line+"\n");
writer.flush();
}
writer.println("${Finished}");
writer.flush();
fileReader.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
public void close() throws IOException{
reader.close();
writer.close();
}
}
package MyChat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class ClientThread extends Thread{
private BufferedReader reader ;
private PrintWriter writer ;
private String name;
public static void main(String[] args) {
new ClientThread();
}
public ClientThread(){
}
@Override
public void run(){
Scanner in = new Scanner(System.in);
try {
Socket socket = new Socket("127.0.0.1", 4700);
System.out.println("You have connected to server!");
System.out.println("Please input your name:");
name = in.nextLine();
//输入名称
reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(socket.getOutputStream());
String line = reader.readLine();
String obj;
if(line.equals("0"))
{
System.out.println("0");
writer.println(name);
writer.flush();//向客户端发送你的姓名;
System.out.println("There is no other client! Please wait connection.");
obj = reader.readLine();//阻塞状态 等待连接
}
else{//连接目标
System.out.println("!0");
writer.println(name);
writer.flush();//向客户端发送你的姓名;
System.out.println(line);
System.out.println("Please input your obj");
while(true) {
obj = in.nextLine();
writer.println(obj);
writer.flush();
if(!reader.readLine().equals("error"))
break;
System.out.println("Error name,please imput again");
}
}
ClientGUI gui = new ClientGUI(reader,writer,obj);//发消息通过gui发送
while(true) {
String str;
str = in.nextLine();
if (str.equals("more"))
new ClientThread().start();
}
} catch (IOException ex) {
System.out.println("Server may have not start!");
System.out.println(ex);
System.exit(-1);
}
}
}
package MyChat;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Server {
public Map<String,Socket> Sockets;
ServerSocket serverSocket;
public static void main(String[] args) {
Server server = new Server();
try{
server.serverSocket = new ServerSocket(4700);
//为保证同时接受访问时不阻塞,一接受访问就创建线程
while(true){
Socket s = server.serverSocket.accept();
System.out.println("once");
server.createThread(s);
}
}
catch (IOException ex){
System.out.println("Could not listen on port:4700");
System.exit(-1);
}
}
public Server(){
Sockets = new ConcurrentHashMap<String,Socket>();
}
public void createThread(Socket socket){
new ServerThread(Sockets,socket).start();
}
}
package MyChat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Map;
public class ServerStreamThread extends Thread{
private BufferedReader reader;
private PrintWriter writer;
private Map<String, Socket> Sockets;
private String name;
public ServerStreamThread(BufferedReader reader, PrintWriter writer, Map<String, Socket> Sockets,String name){
this.writer = writer;
this.reader = reader;
this.Sockets = Sockets;
this.name=name;
}
@Override
public void run(){
try {
while (true) {
String line = reader.readLine();
if(line.equals("close"))
close();
writer.println(line);
writer.flush();
}
}catch (IOException ex){
}
}
public void close(){
try{
writer.println("close");
writer.flush();
writer.close();
reader.close();
Sockets.remove(name);
}catch (IOException ex){
}
}
}
package MyChat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Map;
public class ServerThread extends Thread{
Map<String, Socket> Sockets;
Socket socket;
public ServerThread(Map<String, Socket> Sockets,Socket socket){
this.socket = socket;
this.Sockets = Sockets;
}
@Override
public void run(){
//如果map不为空则创建新线程进行连接
//如果map为空,则一号客户端等待,服务器等待,等新的客户端连接
try {
PrintWriter writer = new PrintWriter(socket.getOutputStream());
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
if (!Sockets.isEmpty()){
printClient(writer);
String name = reader.readLine();
System.out.println("receive");
add(name, socket);
String obj;
Socket objSocket;
while(true) {
obj = reader.readLine();
objSocket= Sockets.get(obj);
if(objSocket!=null) {
writer.println("correct");
writer.flush();
break;
}
writer.println("error");
writer.flush();
}
PrintWriter objwriter = new PrintWriter(objSocket.getOutputStream());
BufferedReader objreader = new BufferedReader(
new InputStreamReader(objSocket.getInputStream()));
objwriter.println(name);
objwriter.flush();
new ServerStreamThread(objreader,writer,Sockets,name).start();
new ServerStreamThread(reader,objwriter,Sockets,obj).start();
//取得 obj socket后与之连接
}
else{
writer.println("0");
writer.flush();
System.out.println("print0");
String name = reader.readLine();
add(name, socket);
System.out.println(name);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void add(String name,Socket s){
Sockets.put(name,s);
}
public boolean printClient(PrintWriter writer){
String names="";
for(Map.Entry<String, Socket> entry : Sockets.entrySet()){
names = names+ entry.getKey()+" ";
}
writer.println(names);
writer.flush();
return true;
}
}