最近闲着无聊,用java socket做了一款简单的即时通讯软件,主要功能如下。
1. 采用C/S模式,使得各个用户通过服务器转发实现聊天的功能。
2. 分为两大模块:客户端模块和服务器端模块。
3. 客户端模块的主要功能:
1)登陆功能:用户可以注册,登录。
2)显示用户:将在线用户显示在列表中。
3)接收信息:能接收其他用户发出的信息。
4)发送信息:能发出用户要发出的信息。
5)私聊功能:选择用户进行私聊。
4.服务器端模块的主要功能:
1)检验登陆信息:检查登陆信息是否正确,并向客户端返回登陆信息,如信息正确。就允许用户登陆。
2)显示在线状态:将该用户的状态发给各在线用户。
3)转发聊天信息:将消息转发给所有在线的用户。
数据库和jdk版本:mysql 5.0 ,jdk1.8
思路为:将服务器内置到客户端里,这样就不用中间服务器。每个人的电脑都是自己的一个服务器,服务器负责接收消息显示在客户端,客户端负责发送用户自己输入的信息给其他人。
数据库表设计:
is_online用来判断用户是否在线,本人设计为在用户登录后将其设为1,然后查询其他用户的is_online,若为1,则在线,将其显示在客户端,点击界面刷新按钮来实时更新在线用户。在退出时,若未点击退出按钮,则默认在线。port 为用户在注册时为每个用户分配的端口,因为本人是在一台电脑上做的,不同客户必须分配不同端口才可通信。
下面是代码主类功能代码:
package main;
import java.awt.BorderLayout;
import java.awt.EventQueue;import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JTextArea;
import javax.swing.JLabel;
import javax.swing.LayoutStyle.ComponentPlacement;
import java.awt.Font;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;import java.awt.ScrollPane;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;public class Talk extends JFrame {
private JPanel contentPane;
private static JTextField textField;
private static JTextArea textArea;
private int id;
private static String username;
private Socket socket;
private static Socket socket_server;
private static ServerSocket serverSocket;
private BufferedReader reader ;
private static PrintWriter writer;
private static BufferedReader reader_server ;
private static PrintWriter writer_server;
private static Scanner scanner = new Scanner(System.in);
private static Scanner scanner_server = new Scanner(System.in);
private static boolean send = false;
private static boolean breakdown=false;
/**
* @wbp.nonvisual location=384,234
*/ /**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Talk frame = new Talk(2,"bb",serverSocket);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public Talk(int id,String username,ServerSocket serverSocket) {
setTitle("聊天");
this.id=id;
this.username=username;
this.serverSocket=serverSocket;
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setBounds(100, 100, 503, 500);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
JLabel lblNewLabel = new JLabel("聊 天 内 容");
lblNewLabel.setBounds(177, 14, 102, 20);
lblNewLabel.setFont(new Font("宋体", Font.PLAIN, 17));
textField = new JTextField();
textField.setBounds(37, 321, 264, 40);
textField.setColumns(10);
//boolean send = false;
JButton btnNewButton = new JButton("发 送");
btnNewButton.setBounds(309, 326, 102, 29);
btnNewButton.setFont(new Font("宋体", Font.PLAIN, 17));
btnNewButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
send=true;
}
});
JButton btnNewButton_2 = new JButton("断开连接");
btnNewButton_2.setBounds(131, 397, 111, 27);
btnNewButton_2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
breakdown=true;
}
});
contentPane.setLayout(null);
//contentPane.add(textArea);
contentPane.add(textField);
contentPane.add(btnNewButton);
contentPane.add(btnNewButton_2);
contentPane.add(lblNewLabel);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(37, 48, 383, 240);
contentPane.add(scrollPane);
textArea = new JTextArea();
scrollPane.setViewportView(textArea);
try {
runsocket();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
startTheSocket1();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
/
public void runsocket() throws IOException{
new Thread(new Runnable() {
@Override
public void run() {
try {
startTheSocket();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
客户端
//开始连接服务端
public void startTheSocket() throws IOException{
toConnectTheServer();//创建Socket并初始化
openTheThreadToReceiveInfoFromServer();//开启一个新的线程来接受服务端发来的信息
String yourMessage=" ";
//一个循环,用于读取键盘的输入
while(true) {
yourMessage =textField.getText(); if(yourMessage!=null&&send) {
textArea.append("\n"+"你说:"+yourMessage);
writer.println(yourMessage);
writer.flush();//记得flush清空缓冲区
send = false;
if (yourMessage.equals("exit")||breakdown) {
disConnect();
}
}
}
}
//创建一个Socket来连接本机的18080端口的服务端并初始化reader和writer
public void toConnectTheServer() throws IOException {
int port = getPort(username);
System.out.println("port="+port+"\n");
socket=new Socket("127.0.0.1",port);
textArea.append("等待链接!");
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
}
public void openTheThreadToReceiveInfoFromServer() throws IOException {
new Thread(new Runnable() {
@Override
public void run() {
try {
printMessage();//打印从服务端收到的信息
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
//循环不断读取服务端的信息
public void printMessage() throws IOException {
boolean flag = false;
while (true) {
String messageFromServer = null;
try {
messageFromServer = reader.readLine();//读取信息
if(messageFromServer.equals("conn")) {
textArea.append("\n"+"连接成功");
flag=true;
} } catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException e) {
}
if(messageFromServer.equals("exit")){
disConnect();//关闭连接
}
}
}
//关闭各种
public void disConnect() throws IOException {
socket.close();
reader.close();
writer.close();
}
///服务器端
public static void startTheSocket1() throws IOException {
acceptTheSocket();//接受连接并且初始化Socket,reader,writer
openTheNewThreadToGetMessageFromClient();
while(true) {
if(breakdown) {
disConnect_server();
breakdown=false;
break;
}
}
}
//接受连接并且初始化Socket,reader,writer
private static void acceptTheSocket() throws IOException {
socket_server=serverSocket.accept();//阻塞方法,用于接受Socket连接
System.out.println("Some one connect me ,it is "+socket_server.getLocalSocketAddress());//打印地址
System.out.println("It's port is "+socket_server.getPort());//打印端口号
reader_server = new BufferedReader(new InputStreamReader(socket_server.getInputStream()));//获得输入流
writer_server = new PrintWriter(new OutputStreamWriter(socket_server.getOutputStream()));//获得输出流
String conn = "conn";
writer_server.println(conn);//发送信息
writer_server.flush();//flush方法用于清空缓冲区,注意的是如果不flush你的信息可能发不出去一直存留在缓冲区
}
//扫描键盘并输入数据
private static void getYourMessage() throws IOException { String yourMessage =textField.getText();
checkYourMessage(yourMessage);//检查发送给客户端的信息
if(send) {
writer.println(yourMessage);//发送信息
writer.flush();//flush方法用于清空缓冲区,注意的是如果不flush你的信息可能发不出去一直存留在缓冲区
}
}
//用于检查是否退出
private static void checkYourMessage(String yourMessage) throws IOException {
//关闭各种
if (yourMessage.equals("exit")) {
socket_server.close();
writer_server.close();
reader_server.close();
System.exit(0);
}
}
//开启一个新的线程来监听客户端发来的信息
private static void openTheNewThreadToGetMessageFromClient() throws IOException {
//开启一个新的线程
new Thread(new Runnable() {
@Override
public void run() {
getTheMessageFromClient();
}
}).start();
}
//不断循环来获取客户端发来的信息
private static void getTheMessageFromClient() {
while(true) {
String messageFromClient = null;
try {
messageFromClient = reader_server.readLine();
} catch (IOException e) {
e.printStackTrace();
}
printTheMessage(messageFromClient);//打印客户端发来的信息
}
}
//打印客户端发来的信息
private static void printTheMessage(String messageFromClient) {
if(messageFromClient!=null)
textArea.append("\n"+username+":"+messageFromClient);
}
private static void disConnect_server() {
writer_server.close();
try {
reader_server.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
///
public int getPort(String name) {
String driver1 = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/socket";
String username = "root";
String password = "cc111";
Connection conn = null;
Statement stmt = null;
try{
Class.forName(driver1);
conn = DriverManager.getConnection(url, username, password);
System.out.println("Connected database successfully...");
stmt = conn.createStatement();
String sql = "select * from users where username = "+"\'"+name+"\'";
ResultSet result=null;
result = stmt.executeQuery(sql);
// 将查询返回的结果集进行遍历输出
while (result.next()) {
return Integer.valueOf(result.getString("port"));
}
}catch(SQLException se){
se.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(stmt!=null)
conn.close();
}catch(SQLException se){
}// do nothing
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}//end finally try
}//end try
return -1; }
}
下面是效果截图:
上面代码仅为主聊天代码,显示在线用户等都没放上去。读者有兴趣可以自行去我的github上下载。
github整个工程代码链接:https://github.com/jiang-congcong/socket-instant_messaging