Java实现聊天室功能
前提
1. UDP(用户数据报协议)
使用UDP协议时,在客户和服务器程序都可以放置一个DatagramSocket(数据报套接字),DatagramSocket 用于收发数据包,而DaragramPacket包含了具体的信息。准备接收一个数据报时,只需提供一个缓冲区,以便安置接收到的数据。数据包抵达时,通过DatagramSocket,作为信息起源地的因特网地址以及端口编号会自动得到
2. IntAddress类
用于描述和包装一个Internet IP 地址,通过三个方法返回 InetAddress 实例
3. Swing类 和 AWT
构建图形界面
服务器端
1. 发送/接收消息
创建 DatagramSocket 对象用于打开指定端口并监听,然后用创建一个 DatagramPacket,利用 DatagramSocket 中的 receive(ds)方法接收数据并放到刚创建的 DatagramPacket 对象中
2. 客户端和服务器端聊天
编写一个简单的聊天图形界面,并在构造函数中用已经创建好的DatagramSocket对象打开特点的端口监听,并用Thread开启Runnable线程。重写run()函数,将接受数据的receive函数写在这里,使程序在任何时候都能够接收客户端发送的数据, 注意数据中包含客户端的IP地址,将它保存起来。另外加一个按钮实现服务器端发送数据,过程和上述客户端发送数据差不多,区别在于上述客户端和服务器端已经connect而现在没有,所以创建DatagramPacket对象使需要在后面加上已经保存的客户端IP地址,然后用send发送数据
3. 客户端和客户端聊天
服务器端不需要知道有多少客户端要连接,所以就不需要多个线程负责接收客户端连接。服务器端不知道客户端什么时候需要连接,所以就需要开启一个线程来接受客户端发送的消息,注意只需要一个线程即可。服务器端收到消息后,需要发给各个客户端,这就需要发送客户端的IP地址,我们知道用receive()方法可以接受数据,DatagramPacket中就包含了IP地址,由于是多个客户端就需要一个集合专门保存各个客户端的IP地址
代码
/**
*@author big_fw
*@version 1.0
*/
package group;
import java.net.DatagramPacket;//实现无连接分组传送服务。 仅基于该数据包中包含的信息,每个消息从一台机器路由到另一台机器
import java.net.DatagramSocket;//用于发送和接收数据报数据包的套接字
import java.net.SocketAddress;
import java.util.ArrayList;
public class gServer implements Runnable{
private DatagramSocket DS;
private int Port = 9998;
private ArrayList<SocketAddress> clients = new ArrayList<SocketAddress>(); //保存客户端IP地址
public gServer() throws Exception{
try {
DS = new DatagramSocket(Port);
new Thread(this).start();
} catch (Exception ex) {
}
}
public void run(){
try{
while(true){
byte[] data = new byte[255];
DatagramPacket DP = new DatagramPacket(data,data.length);
DS.receive(DP);
SocketAddress clientip = DP.getSocketAddress();
if(!clients.contains(clientip)){
clients.add(clientip);
}
this.sendAll(DP);
}
}catch(Exception ex){
}
}
private void sendAll(DatagramPacket dp) throws Exception {
for(SocketAddress sa : clients){
DatagramPacket dd = new DatagramPacket(dp.getData(),dp.getLength(),sa);
DS.send(dd);
}
}
public static void main(String[] args) throws Exception{
new gServer();
}
}
客户端
1. 发送/接收消息
创建DatagramSocket对象,创建好IP地址和端口号后,利用DatagramSocket中的connect(ip,port)方法和服务端建立连接,然后利用DatagramSocket中的send(dp)方法发送早已准备好的数据。
2. 客户端和服务器端
客户端和服务器端都差不多,区别在于,客户端需要在构造函数中先和服务器端建立连接,给服务器端发送一个数据包,表示已经建立连接(其实是告诉服务器端自己的IP地址)然后再打开线程。在run()函数中不用保存服务器地址。在发送消息按钮处不需要给DatagramPacket对象加IP地址,因为客户端早已和服务器端建立连接
3. 客户端和客户端
因为是多个客户端之间进行聊天,所以就需要一个name来区别每一个客户端,打开客户端需要输入昵称。连接服务器、发送消息、接收服务器传来的消息,这些操作和上述的类似,这里不在介绍。( 彩蛋)在客户端GUI中我实现了用回车发送消息,并清空输入框的效果。
代码
客户端1
package group;
import java.awt.BorderLayout; //边界布局设置了一个容器,安排和调整其组件,以适应五个区域:北,南,东,西和中心
import java.awt.event.ActionEvent;//指示组件定义的动作发生的语义事件,当事件发生时,实现ActionListener接口的对象获得此ActionEvent
import java.awt.event.ActionListener;//用于接收动作事件的侦听器界面
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import javax.swing.JFrame;
import javax.swing.JOptionPane;//可以轻松地弹出一个标准对话框,提示用户获取值或通知他们某些东西
import javax.swing.JTextArea;//显示纯文本的多行区域
import javax.swing.JTextField;//一个轻量级组件,允许编辑单行文本
public class gClient extends JFrame implements Runnable,ActionListener {
private JTextField field = new JTextField();
private JTextArea area = new JTextArea("聊天内容:\n");
private String name = null;
private int Port = 9998;
private DatagramSocket DS;
public gClient(){
this.setTitle("客户端");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(area, BorderLayout.CENTER); //private JTextArea area = new JTextArea("聊天内容:\n");
this.add(field, BorderLayout.SOUTH); //private JTextField field = new JTextField();
field.addActionListener(this);
this.setSize(220, 290);
this.setVisible(true);
name = JOptionPane.showInputDialog("输入昵称"); //标准对话框
try {
DS = new DatagramSocket();
InetAddress address = InetAddress.getByName("Localhost");
DS.connect(address,Port);
String str = name + "登录!";
byte[] data = str.getBytes();
DatagramPacket DP = new DatagramPacket(data,data.length);
DS.send(DP);
new Thread(this).start();
} catch (Exception e) {
}
}
public void run(){
try{
while(true){
byte[] data = new byte[255];
DatagramPacket DP = new DatagramPacket(data,data.length);
DS.receive(DP);
String str = new String(DP.getData(),0,DP.getLength());
area.append(str + '\n');
}
}catch(Exception ex){
}
}
public void actionPerformed(ActionEvent e){
try{
String str = name + "说:" + field.getText();
byte[] dd = str.getBytes();
DatagramPacket Data = new DatagramPacket(dd,dd.length);
DS.send(Data);
field.setText("");
}catch(Exception ex){
}
}
public static void main(String[] args){
new gClient();
}
}
客户端2
package group;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class gClient2 extends JFrame implements Runnable,ActionListener {
private JTextField field = new JTextField();
private JTextArea area = new JTextArea("聊天内容:\n");
private String name = null;
private int Port = 9998;
private DatagramSocket DS;
public gClient2(){
this.setTitle("客户端");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(area, BorderLayout.CENTER);
this.add(field, BorderLayout.SOUTH);
field.addActionListener(this);
this.setSize(220, 290);
this.setVisible(true);
name = JOptionPane.showInputDialog("输入昵称");
try {
DS = new DatagramSocket();
InetAddress address = InetAddress.getByName("Localhost");
DS.connect(address,Port);
String str = name + "登录!";
byte[] data = str.getBytes();
DatagramPacket DP = new DatagramPacket(data,data.length);
DS.send(DP);
new Thread(this).start();
} catch (Exception e) {
}
}
public void run(){
try{
while(true){
byte[] data = new byte[255];
DatagramPacket DP = new DatagramPacket(data,data.length);
DS.receive(DP);
String str = new String(DP.getData(),0,DP.getLength());
area.append(str + '\n');
}
}catch(Exception ex){
}
}
public void actionPerformed(ActionEvent e){
try{
String str = name + "说:" + field.getText();
byte[] dd = str.getBytes();
DatagramPacket Data = new DatagramPacket(dd,dd.length);
DS.send(Data);
field.setText("");
}catch(Exception ex){
}
}
public static void main(String[] args){
new group.gClient();
}
}