最近上课老师要我做一个利用GUI编程结合Socket做一个简易的局域网聊天功能,下面来看一看我的思路。
大致分为四个步骤:
1.服务器类的编写
2.服务器线程类的编写
3.客户端类的编写
4.客户端线程类的编写
(GUI编程这一块我就不说了,就是一个简单的界面。下滑有完整代码)
(注:我写的这个程序还具备了查询聊天记录的功能,不会数据库的同学先忽略聊天记录的部分,因为聊天记录涉及到数据库,懂的同学可以根据我的代码自己创建一个数据库。)
1.服务器类的编写
1-0.先将需要用的组件定义为全局
InputStream in; //获取输入流
DataInputStream dataInputStream; //读取输入数据
OutputStream out; //发送输出流
DataOutputStream dataOutputStream; //发送输出流数据
String name = "服务器"; //用户昵称
1-1.首先我们先设置一个端口号,用于客户端的连接
final int PORT = 12345; //设置连接的端口号
1-2.创建服务器的连接
try {
ServerSocket serverSocket = new ServerSocket(PORT); //指定端口
Socket socket_client = serverSocket.accept(); //获取连接
String s = "服务器端...";
InetAddress IP = socket_client.getLocalAddress(); //获取当前本机的IP地址
String ip = IP.toString();
jTextArea_up.append(" "+ip+":"+s+"\n"); //将用户相关信息添加到GUI界面的显示框中
//我这里用的是IO流来读取和发送数据,比较方便
in = socket_client.getInputStream(); //获取输入流
dataInputStream = new DataInputStream(in);
out = socket_client.getOutputStream(); //获取输出流
dataOutputStream = new DataOutputStream(out);
new chat_ServerThread(this).run(); //运行服务器端的线程 作用-显示客户端发送来的信息
}catch (Exception E){
E.printStackTrace();
System.out.println("与客户端连接失败!!!");
}
1-3.创建获取读入、发送、显示框的方法(便于进程类的调用)
//获取输入数据的方法
public DataInputStream getIn(){
return dataInputStream;
}
//获取输入数据的方法
public DataOutputStream getOut(){
return dataOutputStream;
}
//聊天展示框获取
public JTextArea getjTextArea_up(){
return jTextArea_up;
}
1-4.监听发送按钮来执行发送操作
send.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
//向客户端发送内容
DataOutputStream out = getOut(); //创建发送对象
String outContent = jTextArea_down.getText().trim(); //获取完内容之后去除两边的空格
if (outContent.equals("") || out == null) { //内容全部为空值或输出流没有值时直接返回
return;
}
jTextArea_down.setText(""); //获取完用户输入的内容后清空聊天输入框
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss",Locale.CHINA); //添加时间
String dateStr = sdf.format(new Date());
jTextArea_up.append(" " + name +": " + outContent); //将输入的内容添加到聊天框
jTextArea_up.append("\t" + dateStr + "\n");
out.writeUTF(name +": "+ outContent + "\t" + dateStr); //发送内容
jTextArea_down.setFocusable(true);
new upData(name,outContent);
} catch (Exception E) {
E.printStackTrace();
}
}
});
2.服务器线程类的编写(完整代码)
import javax.swing.*;
import java.io.DataInputStream;
public class chat_ServerThread implements Runnable{
JTextArea up; //GUI界面聊天记录的显示框
chat_Server server;
DataInputStream input;
public chat_ServerThread(chat_Server server){ //通过构造方法初始化值
this.server = server;
this.up = server.getjTextArea_up();
}
@Override
public void run() {
try {
//监听读取客户端传来的内容
while(true){
input = server.getIn();
String inputContent=input.readUTF(); //读取内容
if (!inputContent.isEmpty()){ //如果内容不为空
up.append(" "+inputContent+"\n");
}
}
}catch (Exception e ){
e.printStackTrace();
}
}
}
3.客户端类的编写
这里整体和服务器一样,就是客户端的不用创建ServerSocket对象,只用创建socket对象,将服务器的IP地址和端口号传入。
我这里还加了一个循环,用于建立重新连接,当未成功连接上服务器或者断开就会每5秒重连一次
其它地方都与服务器一致。
while(true){
//客户端的连接
final int PORT = 12345; //设置连接的端口号
try {
Socket socket_client = new Socket("localhost",PORT); //获取服务器连接
in = socket_client.getInputStream(); //获取输入流
dataInputStream = new DataInputStream(in);
out = socket_client.getOutputStream(); //获取输出流
dataOutputStream = new DataOutputStream(out);
if (socket_client.isConnected()){ //连接成功
String s = "客户端...";
InetAddress IP = socket_client.getLocalAddress();
String ip = IP.toString();
jTextArea_up.append(" " + ip + ":" + s + "\n");
new chat_ClientThread(this).run();
break;
}else {
Thread.sleep(5000); //断开连接后每五秒重连一次
}
} catch (Exception E) {
E.printStackTrace();
System.out.println("与服务器连接失败!!!");
}
}
4.客户端线程类的编写(完整)
这里和服务器的线程类一样,实现同样的功能
import javax.swing.*;
import java.io.DataInputStream;
public class chat_ClientThread implements Runnable{
JTextArea up;
chat_Client server;
DataInputStream input;
public chat_ClientThread(chat_Client server){
this.server = server;
this.up = server.getjTextArea_up();
}
@Override
public void run() {
try {
//读取客户端传来的内容
while(true){
input = server.getIn();
String inputContent=input.readUTF(); //读取内容
if (!inputContent.isEmpty()){
up.append(" "+inputContent+"\n"); //将读取到的内容添加到显示框内
}
}
}catch (Exception e ){
e.printStackTrace();
}
}
}
下面是其它的完整代码
(服务器类的完整代码)
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class chatSystem_server {
public static void main(String[] args) {
new chat_Server();
}
}
class chat_Server extends JFrame {
JTextArea jTextArea_up;
JButton clear;
JButton send;
JButton star;
JTextArea jTextArea_down;
DataInputStream dataInputStream;
InputStream in;
DataOutputStream dataOutputStream;
OutputStream out;
String name = "服务器"; //用户昵称
public chat_Server() {
//设置主窗口
setTitle("服务器端界面");
setBounds(500, 70, 600, 600);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(null);
//设置不可编辑的文本域
jTextArea_up = new JTextArea();
JScrollPane jScrollPane = new JScrollPane(jTextArea_up);
jScrollPane.setBounds(0, 0, 450, 380);
jTextArea_up.setBounds(0, 0, 450, 380);
jTextArea_up.setEditable(false); //设计不可编辑
jTextArea_up.setLineWrap(true); //自动换行
//操作按钮窗口
JPanel jPanel1 = new JPanel();
star = new JButton("开启连接");
JButton over = new JButton("断开连接");
JButton chat_records = new JButton("聊天记录");
jPanel1.setBounds(450, 0, 140, 100);
jPanel1.add(chat_records);
//用户输入文本框设置
JPanel jPanel2 = new JPanel();
jTextArea_down = new JTextArea(20, 45);
jPanel2.setBounds(0, 380, 450, 200);
jPanel2.add(jTextArea_down);
//输入框的相关操作按钮
JPanel jPanel3 = new JPanel();
clear = new JButton("清空");
send = new JButton("发送");
jPanel3.setBounds(440, 380, 100, 100);
jPanel3.add(clear);
jPanel3.add(send);
//添加组件到主面板
Container container = getContentPane();
container.add(jScrollPane);
container.add(jPanel1);
container.add(jPanel2);
container.add(jPanel3);
//聊天记录按钮的监听
chat_records.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JFrame jFrame = new JFrame();
//设置主窗口
jFrame.setTitle("聊天记录界面");
jFrame.setBounds(520, 100, 465, 420);
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(HIDE_ON_CLOSE);
jFrame.setLayout(null);
jFrame.setResizable(false);
//设置不可编辑的文本域
JTextArea jTextArea_up = new JTextArea(); //创建一个新的
JScrollPane jScrollPane = new JScrollPane(jTextArea_up);
jScrollPane.setBounds(0, 0, 450, 380);
jTextArea_up.setBounds(0, 0, 450, 380);
jTextArea_up.setEditable(false); //设计不可编辑
jTextArea_up.setLineWrap(true); //自动换行
jFrame.add(jScrollPane);
try{
upData.loadDate(jTextArea_up);
}catch (Exception e2){
e2.printStackTrace();
}
}
});
//清空按钮的监听事件
clear.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
jTextArea_down.setText("");
jTextArea_down.setFocusable(true);
}
});
//发送按钮的监听事件
jTextArea_down.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
if ((char)e.getKeyChar()==(KeyEvent.VK_ENTER)){
try {
//向客户端发送内容
DataOutputStream out = getOut();
String outContent = jTextArea_down.getText().trim(); //获取完内容之后去除两边的空格
if (outContent.equals("") || out == null) { //内容全部为空值 //输出流没有值时直接返回
return;
}
jTextArea_down.setText(""); //获取完用户输入的内容后清空聊天输入框
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss",Locale.CHINA); //添加时间
String dateStr = sdf.format(new Date());
jTextArea_up.append(" " + name +": "+ outContent); //将输入的内容添加到聊天框
jTextArea_up.append("\t" + dateStr + "\n");
out.writeUTF(name+": " + outContent + "\t" + dateStr); //发送内容
jTextArea_down.setFocusable(true);
new upData(name,outContent);
} catch (Exception E) {
E.printStackTrace();
}
}
}
});
send.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
//向客户端发送内容
DataOutputStream out = getOut();
String outContent = jTextArea_down.getText().trim(); //获取完内容之后去除两边的空格
if (outContent.equals("") || out == null) { //内容全部为空值 //输出流没有值时直接返回
return;
}
jTextArea_down.setText(""); //获取完用户输入的内容后清空聊天输入框
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss",Locale.CHINA); //添加时间
String dateStr = sdf.format(new Date());
jTextArea_up.append(" " + name +": " + outContent); //将输入的内容添加到聊天框
jTextArea_up.append("\t" + dateStr + "\n");
out.writeUTF(name +": "+ outContent + "\t" + dateStr); //发送内容
jTextArea_down.setFocusable(true);
new upData(name,outContent);
} catch (Exception E) {
E.printStackTrace();
}
}
});
//服务器的连接
final int PORT = 12345; //设置连接的端口号
try {
ServerSocket serverSocket = new ServerSocket(PORT); //指定端口
Socket socket_client = serverSocket.accept(); //获取连接
String s = "服务器端...";
InetAddress IP = socket_client.getLocalAddress();
String ip = IP.toString();
jTextArea_up.append(" "+ip+":"+s+"\n");
in = socket_client.getInputStream(); //获取输入流
dataInputStream = new DataInputStream(in);
out = socket_client.getOutputStream(); //获取输出流
dataOutputStream = new DataOutputStream(out);
new chat_ServerThread(this).run();
}catch (Exception E){
E.printStackTrace();
System.out.println("与客户端连接失败!!!");
}
}
//获取输入数据的方法
public DataInputStream getIn(){
return dataInputStream;
}
//获取输入数据的方法
public DataOutputStream getOut(){
return dataOutputStream;
}
//聊天展示框获取
public JTextArea getjTextArea_up(){
return jTextArea_up;
}
}
class upData {
private static String username="root";
private static String pwd = "123";
private static String url = "jdbc:mysql://localhost:3306/chatsystem";
private static String driver = "com.mysql.jdbc.Driver";
private static Connection conn;
private static Statement stmt;
private static PreparedStatement pstmt;
private static ResultSet rs;
//聊天记录上传
public upData(String name,String content) throws SQLException{
try{
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
String datatime = simpleDateFormat.format(new Date());
Class.forName(driver);
String sql = "insert into chatrecords(name,content,date) values(?,?,?)";
conn = DriverManager.getConnection(url,username,pwd);
pstmt = conn.prepareStatement(sql);
pstmt.setString(1,name);
pstmt.setString(2,content);
pstmt.setString(3,datatime);
// pstmt.setDate(3,new java.sql.Date.valueOf(datatime));
pstmt.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
if (conn!=null){
conn.close();
}
if (pstmt!=null){
pstmt.close();
}
}
}
//聊天记录下载
public static void loadDate(JTextArea jTextArea_up) throws SQLException{
try{
Class.forName(driver);
String sql = "select * from chatsystem.chatrecords";
conn = DriverManager.getConnection(url,username,pwd);
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
while (rs.next()){
Date date = new Date(rs.getTimestamp("date").getTime());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String datetime = simpleDateFormat.format(date);
jTextArea_up.append(" "+rs.getString("name")+"\t"+rs.getString("content")+"\t"+datetime+"\n");
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (conn!=null){
conn.close();
}
if (stmt!=null){
stmt.close();
}
if (rs!=null){
rs.close();
}
}
}
}
(客户端类的完整代码)
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class chatSystem_client {
public static void main(String[] args) throws InterruptedException {
new chat_Client();
}
}
class chat_Client extends JFrame {
JTextArea jTextArea_up;
JButton clear;
JButton send;
JTextArea jTextArea_down;
DataInputStream dataInputStream;
InputStream in;
DataOutputStream dataOutputStream;
OutputStream out;
String name="客户端"; //客户端昵称
public chat_Client() throws InterruptedException {
//设置主窗口
setTitle("客户端界面");
setBounds(500, 70, 600, 600);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(null);
//设置不可编辑的文本域
jTextArea_up = new JTextArea();
JScrollPane jScrollPane = new JScrollPane(jTextArea_up);
jScrollPane.setBounds(0, 0, 450, 380);
jTextArea_up.setBounds(0, 0, 450, 380);
jTextArea_up.setEditable(false); //设计不可编辑
jTextArea_up.setLineWrap(true); //自动换行
//jScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); //设置水平轴隐藏 (设置自动换行会自动隐藏横水平轴)
//操作按钮窗口
JPanel jPanel1 = new JPanel();
JButton star = new JButton("开启连接");
JButton over = new JButton("断开连接");
JButton chat_records = new JButton("聊天记录");
jPanel1.setBounds(450, 0, 140, 100);
// jPanel1.add(star);
//jPanel1.add(over);
jPanel1.add(chat_records);
//用户输入文本框设置
JPanel jPanel2 = new JPanel();
jTextArea_down = new JTextArea(20, 45);
jPanel2.setBounds(0, 380, 450, 200);
jPanel2.add(jTextArea_down);
//输入框的相关操作按钮
JPanel jPanel3 = new JPanel();
JButton clear = new JButton("清空");
JButton send = new JButton("发送");
jPanel3.setBounds(440, 380, 100, 100);
jPanel3.add(clear);
jPanel3.add(send);
//添加组件到主面板
Container container = getContentPane();
container.add(jScrollPane);
container.add(jPanel1);
container.add(jPanel2);
container.add(jPanel3);
//聊天记录按钮的监听
chat_records.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JFrame jFrame = new JFrame();
//设置主窗口
jFrame.setTitle("聊天记录界面");
jFrame.setBounds(520, 100, 465, 420);
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(HIDE_ON_CLOSE);
jFrame.setLayout(null);
jFrame.setResizable(false);
//设置不可编辑的文本域
JTextArea jTextArea_up = new JTextArea(); //创建一个新的
JScrollPane jScrollPane = new JScrollPane(jTextArea_up);
jScrollPane.setBounds(0, 0, 450, 380);
jTextArea_up.setBounds(0, 0, 450, 380);
jTextArea_up.setEditable(false); //设计不可编辑
jTextArea_up.setLineWrap(true); //自动换行
jFrame.add(jScrollPane);
try{
upData.loadDate(jTextArea_up);
}catch (Exception e2){
e2.printStackTrace();
}
}
});
//清空按钮的监听事件
clear.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
jTextArea_down.setText("");
jTextArea_down.setFocusable(true);
}
});
//发送按钮的监听事件
jTextArea_down.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar()==(KeyEvent.VK_ENTER)){
try {
//向客户端发送内容
DataOutputStream out = getOut();
String outContent = jTextArea_down.getText().trim(); //获取完内容之后去除两边的空格
if (outContent.equals("") || out == null) { //内容全部为空值 //输出流没有值时直接返回
return;
}
jTextArea_down.setText(""); //获取完用户输入的内容后清空聊天输入框
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss",Locale.CHINA); //添加时间
String dateStr = sdf.format(new Date());
jTextArea_up.append(" " + name +": "+ outContent); //将输入的内容添加到聊天框
jTextArea_up.append("\t" + dateStr + "\n");
out.writeUTF(name+": " + outContent + "\t" + dateStr); //发送内容
jTextArea_down.setFocusable(true);
new upData(name,outContent);
} catch (Exception E) {
E.printStackTrace();
}
}
}
});
send.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
//向客户端发送内容
DataOutputStream out = getOut();
String outContent = jTextArea_down.getText().trim(); //获取完内容之后去除两边的空格
if (outContent.equals("") || out == null) { //内容全部为空值 || 输出流没有值时直接返回
return;
}
jTextArea_down.setText(""); //获取完用户输入的内容后清空聊天输入框
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss", Locale.CHINA); //添加时间
String dateStr = sdf.format(new Date());
jTextArea_up.append(" "+name+": "+outContent); //将输入的内容添加到聊天框
jTextArea_up.append("\t"+dateStr+"\n");
out.writeUTF(name+": "+outContent+"\t"+dateStr); //发送内容
jTextArea_down.setFocusable(true);
new upData(name,outContent);
} catch (Exception E) {
E.printStackTrace();
}
}
});
while(true){
//客户端的连接
final int PORT = 12345; //设置连接的端口号
try {
Socket socket_client = new Socket("localhost",PORT); //获取服务器连接
in = socket_client.getInputStream(); //获取输入流
dataInputStream = new DataInputStream(in);
out = socket_client.getOutputStream(); //获取输出流
dataOutputStream = new DataOutputStream(out);
if (socket_client.isConnected()){ //连接成功
String s = "客户端...";
InetAddress IP = socket_client.getLocalAddress();
String ip = IP.toString();
jTextArea_up.append(" " + ip + ":" + s + "\n");
new chat_ClientThread(this).run();
break;
}else {
Thread.sleep(5000); //断开连接后每五秒重连一次
}
} catch (Exception E) {
E.printStackTrace();
System.out.println("与服务器连接失败!!!");
}
}
}
//获取输入数据的方法
public DataInputStream getIn() {
return dataInputStream;
}
//获取输入数据的方法
public DataOutputStream getOut() {
return dataOutputStream;
}
//聊天展示框获取
public JTextArea getjTextArea_up() {
return jTextArea_up;
}
}
说明:其实这些代码都很简单,也很容易理解,我们主要学的就是其中的一些思路,思路是很重要的。我现在大一,可能写的不太好,是一个小白都算不上的角色,希望本篇文章能为你提供一些帮助,加油!