您的位置:首页 > 理论基础 > 计算机网络

基于TCP网络通信的简易多线程GUI聊天室

2015-10-06 16:29 260 查看
最近一段时间复习了一下JavaSE的内容,其中发现有不少纰漏。之后针对网络通信和多线程这一块,写了一下基于GUI的简易聊天室程序。

下面就是简单的一个GUI图,丑丑的。。不过基本的功能还是有的,可以简单做练手用。





之前一直弄不好的就是处理多线程和GUI的关系。我认为,要写出好的程序,一个好的架构是很必要的。(因为我自认不会从事前端设计,因此swing awt学习的甚少,只是做了基本了解)因此之前第一次写的时候自己都写糊涂了。之后,我列了一个提纲和简图,总算分清了每一个文件的功能。

1、ichat_server.java:server端的启动,初始化一个server端窗体。

public static void main(String[] args) {
// TODO Auto-generated method stub
ichat_client_frame mainframe = new ichat_client_frame();
mainframe.setVisible(true);
mainframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
2、ichat_server_frame.java:server端窗体的构建程序

private static final long serialVersionUID = 1L;
private JLabel record_label = new JLabel("Chat Record:");
private JTextArea chat_record = new JTextArea();
private JTextArea text = new JTextArea();
private JButton send = new JButton("send");
private JButton open = new JButton("Open");
private ServerSocket server = null;
private Socket client = null;
private JScrollPane jsp = null;
static List<Socket> clients = null;// 保存连接到服务器的客户端

public JTextArea getChatrecord() {
return this.chat_record;
}

public JTextArea getTextcontent() {
return this.text;
}

public ichat_server_frame() throws Exception {
super();
this.setLayout(null);
this.setResizable(false);
this.setTitle("server");
record_label.setBounds(10, 10, 100, 20);
chat_record.setLineWrap(true);
text.setLineWrap(true);
jsp = new JScrollPane(chat_record);
jsp.setBounds(10, 30, 250, 200);
text.setBounds(10, 240, 250, 80);
send.setBounds(270, 240, 100, 40);
send.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
new Thread(new ichat_server_sendthread(ichat_server_frame.this,
client)).start();
// TODO Auto-generated method stub

}
});
open.setBounds(270, 280, 100, 40);
open.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
try {
server = new ServerSocket(8888);
clients = new ArrayList<Socket>();
new Thread(new ichat_server_wait(ichat_server_frame.this,
server, client)).start();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
this.add(record_label);
this.add(jsp);
this.add(text);
this.add(send);
this.add(open);
this.setSize(400, 380);
this.setLocation(50, 50);
}
3、ichat_server_wait.java:进入等待线程。new一个等待线程的代码在Open按钮的响应函数中。在等待线程中接收客户端socket。一旦有连接,则new一个接收线程,不断接收客户端发过来的内容。
private ServerSocket server = null;
private Socket client = null;
private ichat_server_frame isf = null;

public ichat_server_wait(ichat_server_frame isf, ServerSocket server,
Socket client) throws Exception {
// TODO Auto-generated constructor stub
this.server = server;
this.client = client;
this.isf = isf;
}

@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
try {

client = server.accept();
ichat_server_frame.clients.add(client);
new Thread(new ichat_server_receivethread(this.isf, this.client))
.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}


4、ichat_server_sendthread.java:server客户端启动的发送线程,位于send按钮的响应函数下。

private ichat_server_frame isf = null;
private Socket client = null;
private BufferedWriter bw = null;

public ichat_server_sendthread(ichat_server_frame isf, Socket client) {
// TODO Auto-generated constructor stub
this.isf = isf;
this.client = client;
}

@Override
public void run() {
// TODO Auto-generated method stub
if (isf.getTextcontent().getText().equals("")) {
isf.getTextcontent().setText("");
isf.getChatrecord().append("Your input is NULL! \n");
} else {
try {
for (int i = 0; i < ichat_server_frame.clients.size(); i++) {
client = ichat_server_frame.clients.get(i);
bw = new BufferedWriter(new OutputStreamWriter(
this.client.getOutputStream()));
bw.write("Admin" + " : "
+ this.isf.getTextcontent().getText().toString());

bw.newLine();
bw.flush();
}
isf.getChatrecord().append(
"Admin" + " : " + this.isf.getTextcontent().getText()
+ "\n");
isf.getChatrecord().setCaretPosition(
isf.getChatrecord().getText().length());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
isf.getTextcontent().setText("");
}
}


5、ichat_server_receivethread.java:上述提到的server端接收程序。

private Socket client = null;
public ichat_server_frame isf = null;

public ichat_server_receivethread(ichat_server_frame isf, Socket client) {
this.client = client;
this.isf = isf;
}

@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
BufferedReader br = null;
BufferedWriter bw = null;
String Line = null;
try {

br = new BufferedReader(new InputStreamReader(
this.client.getInputStream()));

while ((Line = br.readLine()) != null) {
if (this.client.isClosed()) {
break;
}
isf.getChatrecord().append(Line + "\n");

for (int i = 0; i < ichat_server_frame.clients.size(); i++) {
Socket temp = ichat_server_frame.clients.get(i);
bw = new BufferedWriter(new OutputStreamWriter(
temp.getOutputStream()));
bw.write(Line);
// isf.getChatrecord().append(this.isf.getTextcontent().getText());
bw.newLine();
bw.flush();
}
isf.getChatrecord().setCaretPosition(
isf.getChatrecord().getText().length());
}

br.close();
bw.close();
// client.close();

} catch (IOException e) {
// TODO Auto-generated catch block
try {
br.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
break;
}
}

}


6、ichat_client.java:client端的启动部分,new一个client端窗体。和server端类似,不再赘述。

7、ichat_client_frame.java:client端窗体程序。

private static final long serialVersionUID = 1L;
private JLabel record_label = new JLabel("Chat Record:");
private JTextArea chat_record = new JTextArea();
private JTextArea text = new JTextArea();
private JButton send = new JButton("send");
private JButton connect = new JButton("connect");
private JLabel user_name = new JLabel("user name:");
private JTextField user_name_input = new JTextField("Tourist");
private JScrollPane jsp_client = null;
private boolean flag_test_connect = false;

private Socket client = null;

public JTextField getUsername() {
return this.user_name_input;
}

public JTextArea getChatrecord() {
return this.chat_record;
}

public JTextArea getTextcontent() {
return this.text;
}

public ichat_client_frame() {
super();

try {
client = new Socket("localhost", 8888);
JOptionPane.showMessageDialog(this, "Connect successfully!");
new Thread(new ichat_client_receivethread(ichat_client_frame.this,
client)).start();
this.flag_test_connect = true;
connect.setEnabled(!flag_test_connect);
} catch (Exception e1) {
// TODO Auto-generated catch block
JOptionPane.showMessageDialog(this,
"Connect failed ! Maybe the server hadn't been launched!");
this.flag_test_connect = false;
}
this.setLayout(null);
record_label.setBounds(10, 10, 100, 20);
jsp_client = new JScrollPane(chat_record);
jsp_client.setBounds(10, 30, 250, 200);
text.setBounds(10, 240, 250, 80);
send.setBounds(270, 240, 100, 40);
connect.setBounds(270, 280, 100, 40);
user_name.setBounds(270, 100, 100, 30);
user_name_input.setBounds(270, 130, 100, 30);
chat_record.setLineWrap(true);
text.setLineWrap(true);
this.setTitle("client");
send.setEnabled(flag_test_connect);
send.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
new Thread(new ichat_client_sendthread(ichat_client_frame.this, client)).start();

}
});

connect.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
try {
client = new Socket("localhost", 8888);
flag_test_connect = true;
new Thread(new ichat_client_receivethread(
ichat_client_frame.this, client)).start();
connect.setEnabled(!flag_test_connect);
send.setEnabled(flag_test_connect);
} catch (Exception e1) {
// TODO Auto-generated catch block
JOptionPane.showMessageDialog(ichat_client_frame.this,
"Connect failed, the server hadn'd been launched!");
}
}
});
this.add(record_label);
this.add(jsp_client);
this.add(text);
this.add(send);
this.add(user_name);
this.add(user_name_input);
this.add(connect);
this.setSize(400, 380);
this.setLocation(500, 50);

}


8、ichat_client_sendthread,java:client端发送线程,在send按钮的响应函数里。不再赘述。

9、ichat_client_receivethread.java:client端接收线程,连接server成功后即启动。不再赘述。

以上几大板块便是主要结构。在写的过程中,遇到了几个问题:

1、在线程中接收到了发送来的数据,但是不便于传回主窗体中修改窗体内容。因此我尝试过callable接口带图runnable接口,但是由于其内部的call()方法本身是阻塞的,因此也达不到我预期的效果。

2、比较重要的一点就是利用构造函数传递值。在我自己的写的过程中,我也遇到了传不了、获取 不了主窗体中控件的情况,最后发现是由于控件被定义为了private。。真是教训,以后一定记得住了。

下面再附上几张图吧:

服务器未打开时打开client端



服务器打开时打开client端



聊天界面



感觉之后可以添加的功能还是有很多的,还有线程的安全问题我也没有考虑,之后可以进一步改进。

版权声明:本文为博主原创文章,未经博主允许不得转载。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: