java 实现 仿QQ聊天系统 简易地实现 网络 在线聊天 代码详解(附源代码)
首先,实现的效果如下图:
这是一个非常经典的例题,几乎在每本程序语言入门书中都会涉及到一部分原理的讲解。我在这里简单地介绍一下代码的实现,让刚学习网络编程的读者更好的理解和实现,二来加深自己的记忆。
这个原理无非就是 利用服务器端处理消息再返回给客户端,实现客户端之间的信息交互。 这个原理与生活息息相关,比如两部手机之间的电话交流并不是两部机器直接交流,而是通过信息发射台或者卫星来实现信息交互。虽然原理非常简单,但如果想很完整地实现这个功能并不十分容易(至少对我来讲)。
整个程序代码很简短,主要用到的 java Socket套接字、多线程知识。主要难度在于非常容易产生Exception。
下面附上服务器端代码
package chat;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;
public class ChatServer {
public static ServerSocket serverForClient=null;
public static boolean bool1=false;//用来判断是否连接成功
List<Client> clients=new LinkedList<Client>();
public static void main(String[] args) {
ChatServer start=new ChatServer();
start.runing();
}
public void runing() {//连接客户端,并启动线程
Socket serverSocket=null;//这个名字起得非常不好 和关键字SeverSocket重名了,很容易混淆,建议读者改掉,我这里懒得改了
try {
serverForClient=new ServerSocket(3333);
bool1=true;//当服务器连接成功时 为true
} catch (IOException e1) {
System.out.println("端口被占用,请关闭相关占用程序重启服务器!");
}
try { int count=0;
while(bool1) {
if(count==0) {
System.out.println("*********正在等待客户端连接******");}
count++;
serverSocket =serverForClient.accept();
System.out.println("**第"+count+"个客户端正在连接中……");
Client cc=new Client(serverSocket);
cc.setClient(cc);
new Thread(cc).start();
clients.add(cc);
//new Thread(new Client(serverSocket)).start();
}
} catch (IOException e1) {
e1.printStackTrace();
}finally {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Client implements Runnable{// 启用线程类来 接受客户端
Client me=null;
DataInputStream serverIn=null;
DataOutputStream serverOut=null;
Socket ss=null;
public String accept=null;//这里的字符串名字与accept()方法重名,容易混淆,建议改掉!
public Client(Socket s ){
ss=s;
try {
serverIn=new DataInputStream(ss.getInputStream());
serverOut=new DataOutputStream(ss.getOutputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void send(String sss) {
try {
serverOut.writeUTF(sss);
} catch (IOException e) {
e.printStackTrace();
}
}
public void setClient (Client s) {
me=s;
}
public void run() {
try {
while(bool1) {//服务器端等待接受客户端的消息
accept=serverIn.readUTF();
for(int i=0;i<clients.size();i++) {//把接受到的消息发送给每一个客户端
Client c=clients.get(i);
if(me!=c) {
c.send(accept+"\n");
}
}
//System.out.println(""+accept); //这句话在代码全部完成前可以用来测试服务器是否收到客户端消息
}
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
if(ss!=null) ss.close();
if(serverIn!=null) serverIn.close();
if(serverOut!=null) serverOut.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
服务器端:建立一个接受 客服端连接的 ServerSocket,当有客户端连接时,得到对应的Socket,并传入给线程,线程保存每一个接入的客服端并接受客服端消息,然后发送给其他所有的客户端。(这相当于一个QQ群,当客户端接入相同的服务器IP和端口时,客户端之间即可实现群聊)
下面附上客户端代码
package chat;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowListener;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.*;
public class ChatClient extends JFrame implements ActionListener{
public static boolean bool1=false;//用来判断是否连接成功
JTextField text=new JTextField();
JTextArea area=new JTextArea();
JScrollPane scroll=new JScrollPane(area);
Socket clientSocket=null;
DataOutputStream clientOut=null;
DataInputStream clientIn=null;
public static void main(String[] args) {
new ChatClient().launchJFrame();
}
public void launchJFrame() {
this.setBounds(400, 400, 400, 400);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(scroll,BorderLayout.CENTER);
this.add(text,BorderLayout.SOUTH);
text.addActionListener(this);
connect();
}
public void connect() {
try {
clientSocket=new Socket("localhost",3333);
bool1=true;
clientIn=new DataInputStream(clientSocket.getInputStream());
clientOut=new DataOutputStream(clientSocket.getOutputStream());
System.out.println("连接服务器成功");
new Thread(new Receiver()).start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void actionPerformed(ActionEvent arg0) {
String s=text.getText().trim();
// area.setText(s);
try {
clientOut.writeUTF(""+s);
area.append("我发送的消息:"+s+"\n");
clientOut.flush();
text.setText("");
//clientOut.close();//这里不能关闭否则没法继续向服务器端发送信息
} catch (IOException e) {
e.printStackTrace();
}
}
class Receiver implements Runnable{//内部的类中添加线程:接受服务器消息
public void run() {
while(bool1) {
try {
String clientAccept=clientIn.readUTF();
area.append("接收到的消息:"+clientAccept);
} catch (IOException e) {
try {
if(clientIn!=null) clientIn.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
}
}
客户端:客户端是一个JFrame 窗口,给文本框添加一个ActionListener监视器,将文本框内容输入到输出流中。和服务器端一样创建内部的线程,让线程去获取服务器发送到输出流中的信息并显示在文本区内。
那么客户端和服务器端写好这个程序就基本完成了,先让服务器端运行起来,之后就可以运行多个客户端。客户端将信息发送到服务器,服务器接受并把它发送到其他客户端,即可实现客户端的在线聊天。
到这里来说 功能已经实现了。但是可以预见,即使我试图使用多个catch尽可能地去抓程序中的异常,并使用finally尽量去关闭已经打开且无用的流。我的代码依然有一些问题。
当我关闭一个客户端的时候,服务器端会抛出SocketException异常,虽然这并不影响程序的继续运行。这个问题检查代码用点时间应该也容易解决,不过我已经失去耐心,不想再去花时间处理这种异常问题,读者如果使用我的代码可以在此基础上修改。
下一篇: 文章标题