java 网络编程之使用UDP


本实例使用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());
//		
//	}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值