UDP:将数据及源和目的封装成数据包中,不需要建立连接每个数据报的大小在限制在64k内,因无连接,是不可靠协议不需要建立连接,速度快。
本文两端都用的本地ip。
百度解释:tcp/udp区别。
TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快
一般的聊天程序由于追求快捷的数据传输速度,而又不是比较关注数据的完整性,都是用UDP协议来传递数据,我们聊天的时候就像qq一样,一边打字发消息,一边也在接收消息。
要在程序中实现这种功能,那么就要用到多线程。其中一个线程用来专门接收数据,一个纯种用来专门发送数据。
下面我们就用多线程来实现这个功能:
既然是多线程,那么我们两个主函数里面肯定都有两个线程启动,一个发送,一个接收。
上代码:---------- :
线程类 1 Send 发送数据 :
package com.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Send implements Runnable {
private DatagramSocket datagramSocket = null;
private int port;
// 定义构造方法 使其可以传入 DatagramSocket 和要访问的端口号,然后赋值给变量
Send (DatagramSocket datagramSocket, int port){
this.datagramSocket = datagramSocket;
this.port = port;
}
// 线程
@Override
public void run() {
// 定义一个字符缓冲输入流
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String string = null;
//byte[] data1 = "欢迎你 ! ".getBytes();
while ((string = bufferedReader.readLine()) != null){
// 把输入的字符放入data里面
byte[] data = string.getBytes();
// 把要访问的ip地址,端口号 和 数据放入
DatagramPacket datagramPacket = new DatagramPacket(data, data.length, InetAddress.getByName("localhost"), port);
// 发送数据报到指定 ip地址和端口号
datagramSocket.send(datagramPacket);
// 这里是 : 如果控制台输入 bye 那么代表 跳出循环 不在发送数据 结束
if (string.equals("bye"))
{
// System.out.println("进入if");
break;
}
}
//System.out.println("出来了");
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
if (bufferedReader != null) {
bufferedReader.close(); //关闭流
}
datagramSocket.close(); // 关闭资源
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
线程类 2 Receive 接收数据
package com.socket;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
*
* 接收消息线程
*
*/
public class Receive implements Runnable {
private DatagramSocket datagramSocket = null;
// 定义构造方法
Receive (DatagramSocket datagramSocket){
this.datagramSocket = datagramSocket;
}
// 线程
@Override
public void run() {
try {
while (true) {
// 定义一个1024到字节数组,用于存放发送过来到数据
byte[] data = new byte[1024];
// new 一个数据报
DatagramPacket datagramPacket = new DatagramPacket(data, data.length);
// 阻塞等待数据发送过来
datagramSocket.receive(datagramPacket);
// 获取发送过来的数据的IP地址 和 端口号
String ip = datagramPacket.getAddress().getHostAddress();
int port = datagramPacket.getPort();
// 获取传送过来的数据
String out = new String(datagramPacket.getData(),0, datagramPacket.getLength());
// 如果传送过来的数据是 bye 那么跳出循环 不在接收消息 结束
if ("bye".equals(out)){
System.out.println(" 对方离开了 ! ");
break;
}
// 用来显示发送过来的 IP地址 端口号 内容
System.out.println("来自 ip : "+ip+" 端口号 : "+port + " 内容 : "+out);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
datagramSocket.close(); // 关闭资源
}
}
}
第一个主函数 UDPserver :
package com.socket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UDPserver
{
public static void main(String[] args) {
try {
DatagramSocket send = new DatagramSocket();
DatagramSocket receive = new DatagramSocket(8888); //监听 8888 端口
new Thread(new Send(send,6666)).start(); // 给 6666 端口 发送数据
new Thread(new Receive(receive)).start(); // 监听 8888 端口 获取 从其他端口发送过来的数据
} catch (SocketException e) {
e.printStackTrace();
}
}
}
第二个主函数 UDPclient :
package com.socket;
import java.net.*;
public class UDPclient {
public static void main(String[] args) {
try {
DatagramSocket send = new DatagramSocket();
DatagramSocket receive = new DatagramSocket(6666); //监听 6666 端口
new Thread(new Send(send,8888)).start(); // 给 8888 端口发送数据线程
new Thread(new Receive(receive)).start(); // 监听 6666 端口 获取 从其他端口发送过来的数据
} catch (SocketException e) {
e.printStackTrace();
}
}
}
代码敲完,我们运行来看看结果:
这是UDPclient控制台
接着看UDPserver控制台
已经接收到了 UDPclient 输入的信息,那么我们现在从UDPserver回复几句
看到 UDPclient 收到没
ok,收到了。
不想聊了,走了。
UDPserver 看到“对方离开了” 回复“bye”
此时线程全部停止,程序运行结束。
此时我们注意到,我们的端口号是 55726 这些,是随机的,原因是:两个程序发送信息的DatagramSocket没有绑定端口,所以发送的信息的端口是随机的。
如果我们在两个主函数分别改为
UDPsever :
try {
DatagramSocket send = new DatagramSocket(7777);
DatagramSocket receive = new DatagramSocket(8888);
new Thread(new Receive(receive)).start(); //监听 8888 端口
new Thread(new Send(send,6666)).start(); // 给 6666 端口 发送数据
} catch (SocketException e) {
e.printStackTrace();
}
UDPclient :
try {
DatagramSocket send = new DatagramSocket(9999);
DatagramSocket receive = new DatagramSocket(6666);
new Thread(new Send(send,8888)).start();
new Thread(new Receive(receive)).start();
} catch (SocketException e) {
e.printStackTrace();
}
这样我们就会发现端口号不在随机,而是 7777 和 9999 了,图这个就不贴了。
最后,关于bufferedReader.readLine() 说两句 我们看见while循环的条件
string = bufferedReader.readLine() !=null ,为什么没有输入数据的时候还在循环里面呢? 原因在于readLine()是一个阻塞函数,当没有数据读取时,就会一直阻塞在那,而不是返回 null ,所以我们上面想要退出循环结束程序时, 要么关闭数据流 要么就break 出去。
最后最后,本人也是在校大学生,菜鸟一枚,大神勿喷,大佬有兴趣可以指点指点。
本人第一次写博客,才开始,准备记录自己的学习过程和经历。写的不好,见谅。
:):)