本实例使用UDP设计一个小聊天程序
首先我们应当知道,UDP协议是一种不可靠的传输协议,不想TCP协议建立了可靠的链接
即UDP只管发送不管是送送达,但是我们设计的聊天程序是需要可靠的传输的,所以我们需要拓展UDP的使用
添加两个技术:
1.添加检验功能,保证数据传输的正确性;
2.添加可靠的传输技术,保证数据能传送达到;
程序的界面设计如下:
简单而达到了基本功能的使用
1.添加的校验功能主要是在java 的CRC的基础上进行实现的,利用了JAVA 的自动CRC实现
2.程序的可靠传送我们利用了TCP的思维,在不是建立一个链接,但是我们使其接收端发送一个回执的信息,告诉发送端接受成功,即使没有回执信息,设计发送端在没有接受的回执信息的时候,重复三次发送,在三次发送都没有回执信息的情况下,我们即可认为接收端不可达
程序的源代码:
import java.io.*;
import java.net.*;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.*;
import javax.swing.*;
public class UDPChat implements Runnable, ActionListener{
JTextArea showArea;
JLabel lb11, lb12, lb13;
JTextField msgText, sendPortText, receivePortText, IPAddressText;
JFrame mainJframe;
JButton sendBtn,startBtn;
JScrollPane JSPane;
JPanel pane1, pane2;
Container con;
Thread thread=null;
DatagramPacket sendPack, receivePack;
DatagramSocket sendSocket, receiveSocket;
private InetAddress sendIP;
private int sendPort, receivePort;//储存发送端口和接收端口
private byte inBuf[], outBuf[];
public static final int BUFSIZE=1024;//最大传输包大小
private static final int TIMEOUT = 5000; //设置接收数据的超时时间
private static final int MAXNUM = 3; //设置重发数据的最多次数
public UDPChat ()
{
mainJframe=new JFrame("短信——udp协议");
con=mainJframe.getContentPane();
showArea=new JTextArea();
showArea.setEditable(false);
showArea.setLineWrap(true);
lb11=new JLabel("接收端口号");
lb12=new JLabel("发送端口号");
lb13=new JLabel("对方IP地址");
sendPortText=new JTextField();
sendPortText.setColumns(5);
receivePortText=new JTextField();
receivePortText.setColumns(5);
String ip= GetIP();
IPAddressText=new JTextField(ip);
IPAddressText.setColumns(8);
startBtn=new JButton("开始");
startBtn.addActionListener(this);
pane1=new JPanel();
pane1.setLayout(new FlowLayout());
pane1.add(lb11);
pane1.add(receivePortText);
pane1.add(lb12);
pane1.add(sendPortText);
pane1.add(IPAddressText);
pane1.add(startBtn);
JSPane=new JScrollPane(showArea);
msgText=new JTextField();
msgText.setColumns(40);
msgText.setEditable(false);
msgText.addActionListener(this);
sendBtn=new JButton("发送");
sendBtn.setEnabled(false);
sendBtn.addActionListener(this);
pane2=new JPanel();
pane2.setLayout(new FlowLayout());
pane2.add(msgText);
pane2.add(sendBtn);
con.add(pane1,BorderLayout.NORTH);
con.add(JSPane,BorderLayout.CENTER);
con.add(pane2,BorderLayout.SOUTH);
mainJframe.setSize(600,400);
mainJframe.setVisible(true);
mainJframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainJframe.setLocationRelativeTo(null);
//构建聊天窗口
}
public static void main(String[] args)
{
new UDPChat( );
}
public void actionPerformed(ActionEvent e){
try{
if(e.getSource( )==startBtn){//按下了开始按钮
inBuf=new byte[BUFSIZE];
sendPort=Integer.parseInt(sendPortText.getText());
sendIP=InetAddress.getByName(IPAddressText.getText());
sendSocket=new DatagramSocket();
receivePort=Integer.parseInt(receivePortText.getText());
//创建接收数据包
receivePack=new DatagramPacket(inBuf,BUFSIZE);
//指定接收数据端口
receiveSocket=new DatagramSocket(receivePort);
//创建线程准备接收对方消息
thread=new Thread(this);
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
startBtn.setEnabled(false);
sendBtn.setEnabled(true);
msgText.setEditable(true);
}
else{
//新建监视线程发送数据
new Thread()
{
public void run()
{
String dataString=CheckCRC.AddCRCToken(msgText.getText());
outBuf=dataString.getBytes();
//组装要发送的数据包
sendPack=new DatagramPacket(outBuf,outBuf.length,sendIP,sendPort);
byte[] buf = new byte[1024];
DatagramPacket dp_receive = new DatagramPacket(buf, 1024);
try
{
DatagramSocket sendSocketNew=new DatagramSocket();
sendSocketNew.setSoTimeout(TIMEOUT); //设置发送数据后超时重传的等待时间
int tries = 0; //尝试重发数据的次数
boolean receivedResponse = false; //是否接收到消息回执的信号量
String msgData=msgText.getText();
showArea.append("我说:"+msgData+"\n");
msgText.setText(null);
while(!receivedResponse && tries<MAXNUM){
sendSocketNew.send(sendPack);//发送消息
try{
sendSocketNew.receive(dp_receive);//接收返回的回执
String receiveR=new String(dp_receive.getData(),0,dp_receive.getLength());
if(receiveR.compareTo(dataString)==0)receivedResponse = true;//匹配回执,信号量receivedResponse改为true,退出循环
}catch(InterruptedIOException ex)//超时引发该异常,重发并减少一次重发的次数
{
tries += 1;
showArea.append("消息"+msgData+"发送超时,重试第" + tries+ "次中...\n" );
}
}
if(receivedResponse){
showArea.append("消息"+msgData+"发送成功\n");//收到数据后显示出来
/*由于dp_receive接收数据后,其内部消息长度值变为实际接收的消息的字节数,
于是将dp_receive的内部消息长度重新置为1024*/
dp_receive.setLength(1024);
}else{
showArea.append("无法发送消息"+msgData+"\n");//超时重发MAXNUM次后,仍未获得回执,则放弃发送
}
sendSocketNew.close();
}
catch ( IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
}
catch(UnknownHostException e1){
showArea.append("无法连接到指定地址\n");
}
catch(SocketException e1){
showArea.append("无法打开指定端口\n");
}
catch(IOException e1){
showArea.append("发送数据失败\n");
}
}
public void run( ) {
String msgstr="";
while (true){
try{
receiveSocket.receive(receivePack);
msgstr=new String(receivePack.getData(),0,receivePack.getLength());
if(CheckCRC.CheckCRCToken(msgstr))showArea.append("对方说:"+CheckCRC.GetDataFromToken(msgstr)+"\n");
else showArea.append("对方数据校验出错!\n");
//else showArea.append("对方数据校验出错!"+msgstr+"||"+CheckCRC.AddCRCToken("aa")+"\n");
//接收到数据后发送短信回执
DatagramSocket ds = new DatagramSocket(3000);
String str_send=msgstr;
DatagramPacket dp_send= new DatagramPacket(str_send.getBytes(),str_send.length(),receivePack.getAddress(),receivePack.getPort());
ds.send(dp_send);
ds.close();
}
catch (IOException el){
showArea.append("接收数据出错\n");
}
}
}
/**
* 获取本机IP地址
* @return
* IP地址
*/
protected String GetIP()
{
InetAddress addr;
try
{
addr = InetAddress.getLocalHost();
return addr.getHostAddress().toString();
}
catch (UnknownHostException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return "";
}
}
}
检验功能源代码:
import java.util.zip.CRC32;
public class CheckCRC
{
/**
* 计算出CRC校验码
* @param data
* 数据
* @return
* CRC校验码
*/
private static String GetCRC(String data)
{
CRC32 crc =new CRC32();
crc.update(data.getBytes());
String returnString= Long.toHexString(crc.getValue());
while (returnString.length()<8) returnString="0"+returnString;
while (returnString.length()>8) returnString=returnString.substring(0, 7);
return returnString;
}
/**
* 从Data+CRC中提取CRC
* @param Token
* (即Data+CRC组合)
* @return
* CRC
*/
private static String GetCRCFromToken(String Token)
{
int length=Token.length();
return Token.substring(length-8, length);
}
/**
* 从Data+CRC中提取Data
* @param Token
* (即Data+CRC组合)
* @return
* Data
*/
public static String GetDataFromToken(String Token)
{
int length=Token.length();
return Token.substring(0, length-8);
}
/**
* 将CRC加入data尾部
* @param
* data
* @return
* Token(即Data+CRC组合)
*/
public static String AddCRCToken(String data)
{
return data+GetCRC(data);
}
/**
* 检验数据是否正确
* @param
* Token
* @return
* True or False
*/
public static boolean CheckCRCToken(String Token)
{
if( GetCRC(GetDataFromToken(Token)).compareTo(GetCRCFromToken(Token))==0)return true;
else return false;
}
// public static void main(String[] args)
// {
// String string="aa";
// System.out.println(string+":"+AddCRCToken(string));
// System.out.println("GetCRC:"+GetCRC(string));
// System.out.println("GetData:"+GetData(AddCRCToken(string)));
// System.out.println("GetCRCToken:"+GetCRCToken(AddCRCToken(string)));
// System.out.println(CheckCRCToken(AddCRCToken("aa")));
//
CRC32 crc =new CRC32();
crc.update("aa".getBytes());
String a= Long.toBinaryString(crc.getValue());
System.out.println(a);
System.out.println(a.length());
//
// }
}