前面的博文中已经提到了单工和半双工,而全双工就是能够同时给对方发送数据。比如QQ就是全双工的。本次为了学习全双工,就书写一个简单的聊天工具,本次工具只包括发送消息功能,接收消息功能。不包括登录注册等。本次消息还有一个弊端就是只能一行一行的发送数据。
首先写出大体的界面:
当这个程序运行的时候需要先运行Server.java,而后再运行Client.java。最后再说明一下当程序结束的时候因为程序没有关流的操作所以报异常也很正常。
然后就开始实现功能了,这次大体需要四个类:
首先书写一个收发消息的工具类:
这个类是MessageUtil:
package socket.chat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/**
* @author Hercules
* @version 创建时间:2020年5月29日 上午9:23:43
* 类说明
*/
public class MessageUtil {
public static void sendMessage(Socket socket,String content) {
try {
PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
out.println(content);
} catch (IOException e) {
e.printStackTrace();
}
}
public static String receiveMessgae(Socket socket) {
InputStreamReader isr;
try {
isr = new InputStreamReader(socket.getInputStream());
BufferedReader br = new BufferedReader(isr);
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
而后因为客户端和服务器都要不停的接收消息,这时候就需要一个线程去不停的接收消息:
package socket.chat;
/**
* @author Hercules
* @version 创建时间:2020年5月29日 上午9:23:54
* 类说明
*/
import java.net.Socket;
import javax.swing.JTextArea;
public class Mythread extends Thread{
private JTextArea textArea;
private Socket socket;
public Mythread(JTextArea textArea, Socket socket) {
super();
this.textArea = textArea;
this.socket = socket;
}
@Override
public void run() {
super.run();
while (true) {
String content = MessageUtil.receiveMessgae(socket);
textArea.append(content);
textArea.append("\n\n");
}
}
}
客户端代码如下:
package socket.chat;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
/**
* @author Hercules
* @version 创建时间:2020年5月29日 上午9:21:42
* 类说明
*/
public class Client {
static Socket socket = null;
public static void main(String[] args) {
try {
socket = new Socket("127.0.0.1",9999);
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
//给出一个顶层容器,并设置窗口标题为聊天窗口
JFrame jFrame = new JFrame("我是客户端");
//设置聊天窗口的大小
jFrame.setSize(300,400);
//设置聊天窗口在屏幕上居中显示
jFrame.setLocationRelativeTo(null);
//设置点击窗口的x自动退出程序,如果不写这一行代码,则只会界面消失,但是不会退出
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//给出一个一般容器JPanel
JPanel panel = new JPanel();
//将一般容器放入顶层容器
jFrame.add(panel);
//将一般容器的布局设置为四边布局
panel.setLayout(new BorderLayout());
//初始化一个文本输入区域,作为消息显示区域
JTextArea show = new JTextArea();
//给显示区域设置一下大小,因为四边布局宽是默认铺满的,所以只设置高260
show.setPreferredSize(new Dimension(0,260));
//再给显示区域设置一下边框,可以更好的区分,这里是实线边框
show.setBorder(BorderFactory.createLineBorder(Color.BLACK));
//而后显示区域应该是不可编辑的,这里设置不可编辑
show.setEditable(false);
//将文本输入区域添加到一般容器中,并设置其显示在容器的北方,也就是上方
//四边布局分东南西北,记住一个原则,上北下南左西右东
panel.add(show,BorderLayout.NORTH);
//初始化一个按钮
JButton send = new JButton("发送");
//在容器中加入按钮并且,设置按钮在布局的南边,也就是下面
panel.add(send,BorderLayout.SOUTH);
//再初始化一个文本输入区域作为输入区域
JTextArea textArea = new JTextArea();
//将这个文本输入区域放在四边布局的中央
panel.add(textArea,BorderLayout.CENTER);
//设置send按钮的事件
send.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//获取输入框的内容
String text ="客户端:" + textArea.getText();
//将输入框的内容显示到显示框中并且换行
show.append(text+"\n");
//发送消息
MessageUtil.sendMessage(socket, text);
//清空输入框
textArea.setText("");
}
});
//启动线程不停的接收消息
new Mythread(show, socket).start();;
//最后再显示一下这个顶层容器
jFrame.setVisible(true);
}
}
服务器端代码如下:
package socket.chat;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
/**
* @author Hercules
* @version 创建时间:2020年5月29日 上午9:21:53
* 类说明
*/
public class Server {
static Socket socket = null;
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(9999);
socket = serverSocket.accept();
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
//给出一个顶层容器,并设置窗口标题为聊天窗口
JFrame jFrame = new JFrame("我是服务器");
//设置聊天窗口的大小
jFrame.setSize(300,400);
//设置聊天窗口在屏幕上居中显示
jFrame.setLocationRelativeTo(null);
//设置点击窗口的x自动退出程序,如果不写这一行代码,则只会界面消失,但是不会退出
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//给出一个一般容器JPanel
JPanel panel = new JPanel();
//将一般容器放入顶层容器
jFrame.add(panel);
//将一般容器的布局设置为四边布局
panel.setLayout(new BorderLayout());
//初始化一个文本输入区域,作为消息显示区域
JTextArea show = new JTextArea();
//给显示区域设置一下大小,因为四边布局宽是默认铺满的,所以只设置高260
show.setPreferredSize(new Dimension(0,260));
//再给显示区域设置一下边框,可以更好的区分,这里是实线边框
show.setBorder(BorderFactory.createLineBorder(Color.BLACK));
//而后显示区域应该是不可编辑的,这里设置不可编辑
show.setEditable(false);
//将文本输入区域添加到一般容器中,并设置其显示在容器的北方,也就是上方
//四边布局分东南西北,记住一个原则,上北下南左西右东
panel.add(show,BorderLayout.NORTH);
//初始化一个按钮
JButton send = new JButton("发送");
//在容器中加入按钮并且,设置按钮在布局的南边,也就是下面
panel.add(send,BorderLayout.SOUTH);
//再初始化一个文本输入区域作为输入区域
JTextArea textArea = new JTextArea();
//将这个文本输入区域放在四边布局的中央
panel.add(textArea,BorderLayout.CENTER);
//设置send按钮的事件
send.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//获取输入框的内容
String text = "服务器:"+textArea.getText();
//将输入框的内容显示到显示框中并且换行
show.append(text+"\n");
//发送消息
MessageUtil.sendMessage(socket, text);
//清空输入框
textArea.setText("");
}
});
//启动线程不停的接收消息
new Mythread(show, socket).start();;
//最后再显示一下这个顶层容器
jFrame.setVisible(true);
}
}
运行结果如下:
这时候就可以同时发送消息了,这就是全双工,接下来的博客,会有一个比较正式的聊天软件项目。