**
聊天室的实现,基本步骤解析
**
总体思想结构
1.建立服务器端,服务器端不需要界面
建立一个包为服务器包 serve
包内应该有服务器的主方法用来启动服务器,和一个用来接收客户端消息并且转发给所有的客户端的线程类 服务器给所有客户端发消息的线程
2.建立客户端,有界面
建立一个包为客户端包 cient
包内应该有客户端的界面类,可以单独的运行(即有主方法),并且需要一个向服务器发送消息的线程类和一个发送消息的方法,主体大致结构如下
下面进行更深一步的的讲解
客户端:
CientUI.java类:
构建自己喜欢的swing界面;
为按钮添加监听器;(主要逻辑代码)
可以有一个连接服务器的方法
package cient;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/**
* 客户端的主界面类
* @author VastWu
*
*/
public class CientUI {
private Socket socket;
public SendUtil su;
public static void main(String[] args) {
CientUI cientUI=new CientUI();
cientUI.init();
}
//连接服务器的方法
public Socket connection() {
try {
//尝试连接服务器
Socket socket=new Socket("127.0.0.1", 100);
return socket;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void init() {
JFrame jframe=new JFrame("714皇家赌场");
jframe.setSize(700, 500);
jframe.setDefaultCloseOperation(3);
jframe.setLayout(null);
//设置界面居中
jframe.setLocationRelativeTo(null);
//设置最大化按钮不可用
jframe.setResizable(false);
//历史消息框、并且添加滚动条
JTextArea historyMsg=new JTextArea();
JScrollPane jsp=new JScrollPane(historyMsg);
jsp.setBounds(5, 25, 685, 300);
historyMsg.setEditable(false);
jframe.add(jsp);
//发送消息框
JTextArea sendMsg=new JTextArea();
JScrollPane jsp2=new JScrollPane(sendMsg);
jsp2.setBounds(5, 325, 600, 140);
jframe.add(jsp2);
//按钮
JButton sendBut=new JButton("发送");
JButton clearBut=new JButton("清空");
JButton connectBut=new JButton("连接服务器");
sendBut.setBounds(605, 364, 84, 100);
clearBut.setBounds(605, 325, 84, 40);
connectBut.setBounds(5, 1, 150, 22);
jframe.add(connectBut);
jframe.add(sendBut);
jframe.add(clearBut);
//给按钮添加监听器
ActionListener al=new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String command=e.getActionCommand();
switch (command) {
case "发送":
if(socket!=null) {
String msg=sendMsg.getText().trim();
sendMsg.setText("");
historyMsg.append("我说:"+msg+"\r\n");
//调用方法发送出去
su.send(msg);
}
break;
case "清空":
sendMsg.setText("");
break;
case "连接服务器":
socket=CientUI.this.connection();
if(socket==null) {
JOptionPane.showMessageDialog(null, "连接失败,请检查网络或重新连接!");
}else {
connectBut.setText("已连接服务器");
JOptionPane.showMessageDialog(null, "连接成功!");
//启动线程并且给服务器发送消息
ReceiveThread rt=new ReceiveThread(socket, historyMsg);
rt.start();
su=new SendUtil(socket);
}
break;
default:
break;
}
}
};
connectBut.addActionListener(al);
sendBut.addActionListener(al);
clearBut.addActionListener(al);
jframe.setVisible(true);
}
}
ReceiveThread.java类:
接收服务器信息的线程类并且把消息加到界面中
package cient;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import javax.swing.JTextArea;
/**
* 接收来自服务器的线程类
* @author VastWu
*
*/
public class ReceiveThread extends Thread{
private Socket socket;
private JTextArea historyMsg;
public ReceiveThread(Socket socket,JTextArea historyMsg) {
this.socket=socket;
this.historyMsg=historyMsg;
}
@Override
public void run() {
try {
InputStream ips=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(ips);
BufferedReader br=new BufferedReader(isr);
while(true) {
String msg=br.readLine();
historyMsg.append(msg+"\r\n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
SendUtil类:
客户端发送消息的类
package cient;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
/**
* 发送消息的类
* @author VastWu
*
*/
public class SendUtil {
private Socket socket;
private BufferedWriter bw;
private String add;
public SendUtil(Socket socket) {
this.socket=socket;
try {
OutputStream ops=socket.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(ops);
bw=new BufferedWriter(osw);
add=socket.getLocalSocketAddress().toString();
} catch (Exception e) {
e.printStackTrace();
}
}
public void send(String msg) {
try {
bw.write(add+"说:"+msg+"\r\n");
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端:
Serve.java类:
启动一个服务器的类
package serve;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
/**
* 服务器端
* @author VastWu
*
*/
public class Serve {
//声明一个集合用于储存socket对象
public static ArrayList<Socket> socketList=new ArrayList<Socket>();
//直接主方法以启动服务器
public static void main(String[] args) throws Exception {
//使用一个服务端套接字建立一个端口
ServerSocket serverSocket=new ServerSocket(100);
System.out.println("服务器开启成功,正在等待连接。。。");
SendAllThread sat=new SendAllThread();
sat.start();
//为了使socket对象不被覆盖这里使用while死循环对socket进行储存
while(true) {
//等待连接
Socket socket=serverSocket.accept();
//连接成功后存入集合内
socketList.add(socket);
//得到客户端的地址
String address=socket.getRemoteSocketAddress().toString();
//连接成功后控制台提示连接成功
System.out.println(address+"连接上来了");
//启动转发线程
TurnSendThread tst=new TurnSendThread(socket);
tst.start();
//启动通知线程
TellThread tt=new TellThread(socket);
tt.start();
}
}
}
SendAllThread.java类:
服务器主动给服务器发信息的线程类
package serve;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;
/**
* 给所有客户端发消息的线程类
* @author VastWu
*
*/
public class SendAllThread extends Thread{
@Override
public void run() {
try {
//服务器控制台发送消息
Scanner scan=new Scanner(System.in);
//因为要发多条消息,所以用死循环
while(true) {
//接收输入的一行消息
String msg=scan.nextLine();
//使用输出流进行处理,并且需要向所有用户发消息所以需要对集合进行循环发送
for(Socket socket:Serve.socketList) {
OutputStream ops=socket.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(ops);
BufferedWriter bw=new BufferedWriter(osw);
bw.write("服务器说:"+msg+"\n");
bw.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
TurnSendThread.java类:
转发消息的线程类,即把客服端发过来的信息转发给所有的客户端
package serve;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
/**
* 转发客服端发送过来的消息的线程类
* @author VastWu
*
*/
public class TurnSendThread extends Thread{
private Socket socket;
private String address;
//先使用构造方法把socket传过来
public TurnSendThread(Socket socket) {
this.socket=socket;
//客户端的地址
address=socket.getRemoteSocketAddress().toString();
}
@Override
public void run() {
try {
InputStream ips=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(ips);
BufferedReader br=new BufferedReader(isr);
while(true) {
String str=br.readLine();
//将读到的消息发送给所有的客户端
for(Socket sk:Serve.socketList) {
if(sk!=socket) {
OutputStream ops=sk.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(ops);
BufferedWriter bw=new BufferedWriter(osw);
bw.write(str+"\n");
bw.flush();
}
}
}
} catch (Exception e) {
// 如果抛出异常,说明连接已经断开,此处为处理用户离开时通知其他客户端的功能
// 从List中删除该客户端,并通知其他客户端
Serve.socketList.remove(this.socket);
for (Socket sk : Serve.socketList) {
try {
// 从sk上获得输出流
OutputStream ops = sk.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(ops);
BufferedWriter bw = new BufferedWriter(osw);
bw.write(address + "离开了房间!!\n");
bw.flush();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
这样子就基本能实现群聊了嘻
下面是拓展:
实现一个客户端连接服务端以后,服务器通知其他所有客户端有人上线
即在服务器写一个发信息的线程类,先给连上来的客服端发欢迎消息,然后给其他所有人发送通知。
package serve;
import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
/**
*服务器发送通知的线程类
* @author VastWu
*
*/
public class TellThread extends Thread{
private Socket socket;
public TellThread(Socket socket) {
this.socket=socket;
}
@Override
public void run() {
try {
OutputStream ops=socket.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(ops);
BufferedWriter bw=new BufferedWriter(osw);
String address=socket.getRemoteSocketAddress().toString();
bw.write("欢迎"+address+"来到聊天室\n");
bw.write("当前在线人数:"+Serve.socketList.size()+"人\r\n");
bw.flush();
for(Socket sk:Serve.socketList) {
if(sk!=socket) {
OutputStream ops2=sk.getOutputStream();
OutputStreamWriter osw2=new OutputStreamWriter(ops2);
BufferedWriter bw2=new BufferedWriter(osw2);
bw2.write(address+"进入了聊天室,当前在线人数:"+Serve.socketList.size()+"人\r\n");
bw2.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
功能实现:
更改ip地址即可实现同局域网的聊天…