本文爲使用UDP 進行數據傳輸的一個聊天室應用,使用JAVA語言開發的聊天室在目前的實際中應用很少,寫這個例子的原因的進一步理解CS模式編程,掌握這種思想。
本例子分爲客戶端和服務器兩個部分,客戶端輸入想要發送給的對象(IP地址)已經要發送的數據,發到服務器中,服務器對發送來的數據進行解析後轉發到相應的客戶端,客戶端與客戶端之間不進行直接的通訊,而是由服務器轉發,本例子中的服務器轉發部分可以再優化(如果不優化,在用戶量大的時候轉發效率非常低,這個在實際運用時再實際修改),客戶端實現的是通過服務器向指定的客戶端發送消息(實際運用中可以使用網段發送,即是向指定網段的所有客戶端發消息),服務器則可以向所有的在線客戶端推送消息。在例子中的一個主要思想是客戶端與客戶端的通訊須經過服務器端,相當於要在服務器過濾之後才進行發送。在實際的應用中客戶端不能知道另一個客戶端的IP地址,但是每個客戶端的知道服務器的IP,我們可以用一些唯一標識(比如賬號或者用戶名)來標識我們的每個客戶端,這樣每個客戶端只要知道對方的賬號就可以進行通信了。
好了,不說那麼多了,直接上代碼,
雖然些得渣渣的,但也是自己一筆一字碼出來的,望各位大神指點指點。
服務器端的代碼
package com.gxuwz.fulunyong;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
* @author:fulunyong
* @email:[email protected]
* @datetime:2015年8月21日下午7:55:00
* @version 1.0
* @serial 服務器
*/
public class ChatServer extends JFrame implements ActionListener{
private static final long serialVersionUID = 4717084330471059797L;
private Integer port=2999;//系統端口
private JTextArea info;//客戶端發來的消息
private JTextField msg;//推送的消息
private JButton send;//推送按鈕
private List<User> users;//系統列表
private DatagramSocket socket;//傳輸介質
public ChatServer(){
setTitle("梧州學院聊天室-服務器");
setSize(400, 600);//設置窗體的大小
setLocationRelativeTo(null);//居中出現
setDefaultCloseOperation(EXIT_ON_CLOSE);//點擊關閉時推出程序
//組件初始化
info=new JTextArea();
info.setEditable(false);
msg=new JTextField();
send=new JButton("推送");
send.addActionListener(this);//給按鈕添加監聽器
this.add(info, BorderLayout.CENTER);//把消息顯示框添加到窗體的居中位置
JPanel jPanel=new JPanel(new BorderLayout());//新建一個使用盒式佈局的面板
jPanel.add(msg, BorderLayout.CENTER);//把消息輸入框添加到面板居中位置
jPanel.add(send, BorderLayout.EAST);//把推送按鈕添加到面板的東方向
this.add(jPanel, BorderLayout.SOUTH);//把面板添加到窗體的南方向。
//初始化數據報套接字
try {
socket=new DatagramSocket(port);
} catch (SocketException e){
e.printStackTrace();
return;//如果出現異常,程序退出,窗體不應該再顯示。
}
users=new ArrayList<User>();//
info.append("服務器啓動成功,等待客戶端連接。。。\n");//執行到這裏證明已經成功啓動服務器。
setVisible(true);//顯示窗體
byte[] bt=new byte[1024*64];
//循環讀取並轉發客戶端的連接及發送的數據
while(true){
DatagramPacket packet=new DatagramPacket(bt, bt.length);
try {
socket.receive(packet);
} catch (IOException e1){
e1.printStackTrace();
}
if (null!=packet){
User tempuser=new User(packet.getAddress().toString().substring(1), packet.getPort());
if (!users.contains(tempuser)){//
users.add(tempuser);
}
String temp=new String(packet.getData(), 0, packet.getLength());
String targetIp=temp.substring(0, temp.indexOf(":"));
String str=temp.substring(temp.indexOf(":")+1);
info.append(packet.getAddress().toString().substring(1)+" 對 "+targetIp+" 說:\n");
info.append(str+"\n");
for(int i = 0; i <users.size(); i++){
User user=users.get(i);
if(targetIp.equals(user.getUserIp())){//查找消息接收對象
byte[] bs=(packet.getAddress().toString().substring(1)+temp.substring(temp.indexOf(":"))).getBytes();
try {
DatagramPacket datagramPacket=new DatagramPacket(bs, bs.length, InetAddress.getByName(user.getUserIp()), user.getUserPort());
socket.send(datagramPacket);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
@Override
public void actionPerformed(ActionEvent e){
String command=e.getActionCommand();
//推送按鈕事件,服務器主動給所有用戶發送消息。
if("推送".equals(command)){
for(int i = 0; i <users.size(); i++){
User user=users.get(i);
String str="服務器:"+msg.getText();
byte[] bs=str.getBytes();
try {
DatagramPacket packet=new DatagramPacket(bs, bs.length, InetAddress.getByName(user.getUserIp()), user.getUserPort());
socket.send(packet);
} catch (Exception e1){
e1.printStackTrace();
}
}
}
}
public static void main(String[] args){
new ChatServer();
}
}
客戶端的代碼:
package com.gxuwz.fulunyong;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
* @author:fulunyong
* @email:[email protected]
* @datetime:2015年8月21日下午7:56:25
* @version 1.0
* @serial 客戶端
*/
public class ChatClient extends JFrame {
private static final long serialVersionUID = -4460571879641744285L;
private JTextField toIp;//所要發送消息的對象
private JTextArea info;//消息顯示框
private JTextField msg;//要發送的消息
private JButton send;//發送按鈕
private DatagramSocket socket;//數據報套接字
private Integer port=6688;//客戶端的端口
private Integer serverPort=2999;//服務器的端口,根據服務器實際端口修改
private String serverIp="10.16.151.90";//服務器IP 根據具體IP修改
public ChatClient(){
setTitle("梧州學院聊天室-客戶端");//設置標題
setSize(400, 600);//設置窗口大小
setLocationRelativeTo(null);//居中出現
setDefaultCloseOperation(EXIT_ON_CLOSE);//關閉時推出
//組件初始化
toIp=new JTextField("127.0.0.1");//默認發送的IP,默認是本機,可以在客戶端修改
info=new JTextArea();
info.setEditable(false);//禁用編輯
msg=new JTextField();
send=new JButton("發送");
//給發送按鈕添加事件
send.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e){
String str=toIp.getText()+":"+msg.getText();
byte[] buf=str.getBytes();
try {
//拼裝數據報
DatagramPacket packet=new DatagramPacket(buf, buf.length, InetAddress.getByName(serverIp), serverPort);
socket.send(packet);//發送數據報
} catch (Exception e1) {
e1.printStackTrace();
}
info.append("我說:"+msg.getText()+"\n");
msg.setText("");//清空消息輸入框
msg.requestFocus();//獲取鼠標焦點
}
});
this.add(toIp,BorderLayout.NORTH);//添加IP輸入框到窗體的北方向。
this.add(info,BorderLayout.CENTER);//添加消息顯示框到居中位置
JPanel panel=new JPanel(new BorderLayout());//新建使用盒式佈局的面板
panel.add(msg, BorderLayout.CENTER);//把消息輸入框添加到面板的居中位置
panel.add(send, BorderLayout.EAST);//把發送按鈕添加到面板的東方向
this.add(panel, BorderLayout.SOUTH);//把面板添加到窗體的南方向
//啓動數據報套接字
try {
socket=new DatagramSocket(port);
} catch (SocketException e1){
e1.printStackTrace();
return;//如果出錯,程序退出。
}
setVisible(true);//顯示窗體
byte[] bs=new byte[1024*64];//新建字節數組暫存數據
DatagramPacket packet=new DatagramPacket(bs, bs.length);//新建一個數據報對象
//循環讀取服務器發來的消息。
while(true){
try {
socket.receive(packet);//讀取消息
if(null!=packet){//不爲空就顯示的消息顯示框
String str=new String(packet.getData(), 0, packet.getLength());
info.append(str+"\n");
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
public static void main(String[] args){
new ChatClient();
}
}
用戶類
package com.gxuwz.fulunyong;
import java.io.Serializable;
/**
* @author:fulunyong
* @email:[email protected]
* @datetime:2015年8月21日下午8:00:29
* @version 1.0
* @serial 用戶
*/
public class User implements Serializable{
private static final long serialVersionUID =-2059184085514940866L;
private String userIp;
private Integer userPort;
public User(){
}
public User(String userIp, Integer userPort) {
this.userIp = userIp;
this.userPort = userPort;
}
public String getUserIp() {
return userIp;
}
public void setUserIp(String userIp) {
this.userIp = userIp;
}
public Integer getUserPort() {
return userPort;
}
public void setUserPort(Integer userPort) {
this.userPort = userPort;
}
@Override
public String toString() {
return "User [userIp=" + userIp + ", userPort=" + userPort + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((userIp == null) ? 0 : userIp.hashCode());
result = prime * result + ((userPort == null) ? 0 : userPort.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (userIp == null) {
if (other.userIp != null)
return false;
} else if (!userIp.equals(other.userIp))
return false;
if (userPort == null) {
if (other.userPort != null)
return false;
} else if (!userPort.equals(other.userPort))
return false;
return true;
}
}