Java网络编程实践和总结 --- 基于UDP的Socket编程
2015-04-02 19:54
621 查看
前面的blog介绍和总结基于TCP协议的网络编程。TCP提供了一种可靠的传输方式。如果数据在传输过程丢失或者损坏了,TCP会保证数据会再次发送,直到数据完整接受。如果数据包以乱序到达目的地,TCP也会根据数据包的原始顺序将其正常排序。如果传输的速度过快而导致数据丢包,TCP也会自动地调整速度,保证稳定不丢包。因此程序不用担心数据的问题,但往往这也意味着TCP协议的网络程序需要牺牲性能来实现这些功能。建立和销毁一个TCP连接往往都是需要付出很大的性能代价的。
今天我们要讲的用户数据报协议(User Datagram Protocol, UDP)协议是建立在IP协议之上的另一种协议。特点是速度快,但不稳定可靠。UDP不需要想TCP那样建立稳定的连接,不关心数据是否在传输的过程丢失,不关心数据在传输过程中顺序,不关心数据能够被对方接受的到。
在实际的需求中往往就需要用到UDP数据,如在线实时语音和视频时,在保证速度的前提下部分的数据丢失所造成的影响不大。但如果使用TCP协议,为了等待个别数据包而导致请求重传或等待数据包所造成的停顿是无法接受的。
TCP和UDP的区别就好比打电话和寄邮件的区别。打电话必须要双方确认的情况下才能进行通话,而寄邮件不管所寄对方是否在线或是否存在,一旦寄出就可以不用管了。
Java中UDP通过两个类来实现:
DatagramPacket类 和 DatagramSocket类
DatagramPacket类用于将数据字节填充到其中的UDP包中,用于发送和解包接受的数据包。
DatagramSocket类用于将数据UDP包进行发送和接受操作等。
之前学习TCP Socket编程时实现了Echo回显操作网络程序,现在我们通过UDP来重新实现:
以下为客户端程序:
以下为服务端程序:
测试结果:
分别打开服务端和客户端,客户端输入信息。
以下为客户端显示结果:
to localhost/10000 : hello
to localhost/10000 : from 127.0.0.1/10000 : HELLO
world
to localhost/10000 : from 127.0.0.1/10000 : WORLD
test
to localhost/10000 : from 127.0.0.1/10000 : TEST
over
EchoSenderThread end : localhost/127.0.0.1
from 127.0.0.1/10000 : OVER
EchoReceiverThread end : localhost/127.0.0.1
以下为服务端显示结果:
receive a datagram packet
from hostname : /127.0.0.1, port : 50730 : hello
sent to hostname : /127.0.0.1, port : 50730 : HELLO
receive a datagram packet
from hostname : /127.0.0.1, port : 50730 : world
sent to hostname : /127.0.0.1, port : 50730 : WORLD
receive a datagram packet
from hostname : /127.0.0.1, port : 50730 : test
sent to hostname : /127.0.0.1, port : 50730 : TEST
receive a datagram packet
from hostname : /127.0.0.1, port : 50730 : over
sent to hostname : /127.0.0.1, port : 50730 : OVER
客户端发送信息后可以收到服务端大写的回显。
今天我们要讲的用户数据报协议(User Datagram Protocol, UDP)协议是建立在IP协议之上的另一种协议。特点是速度快,但不稳定可靠。UDP不需要想TCP那样建立稳定的连接,不关心数据是否在传输的过程丢失,不关心数据在传输过程中顺序,不关心数据能够被对方接受的到。
在实际的需求中往往就需要用到UDP数据,如在线实时语音和视频时,在保证速度的前提下部分的数据丢失所造成的影响不大。但如果使用TCP协议,为了等待个别数据包而导致请求重传或等待数据包所造成的停顿是无法接受的。
TCP和UDP的区别就好比打电话和寄邮件的区别。打电话必须要双方确认的情况下才能进行通话,而寄邮件不管所寄对方是否在线或是否存在,一旦寄出就可以不用管了。
Java中UDP通过两个类来实现:
DatagramPacket类 和 DatagramSocket类
DatagramPacket类用于将数据字节填充到其中的UDP包中,用于发送和解包接受的数据包。
DatagramSocket类用于将数据UDP包进行发送和接受操作等。
之前学习TCP Socket编程时实现了Echo回显操作网络程序,现在我们通过UDP来重新实现:
以下为客户端程序:
public class UDPEchoClient { public static final String END_FLAG = "over"; public static void main(String[] args) throws IOException { EchoSenderThread senderThread = new EchoSenderThread(); EchoReceiverThread receiverThread = new EchoReceiverThread(senderThread.getTheSocket()); senderThread.start(); receiverThread.start(); } } //客户端的发送线程,用于从标准输入获取信息,通过UDP发送到服务端 class EchoSenderThread extends Thread { //连接的UDP Socket DatagramSocket theSocket; public EchoSenderThread() throws SocketException { this.theSocket = new DatagramSocket(); //连接到默认的服务器 theSocket.connect(new InetSocketAddress(UDPEchoServer.DEFAULT_HOSTNAME, UDPEchoServer.DEFAULT_PORT)); } @Override public void run() { //标准输入流 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); while (true) { try { System.out.print("to " + theSocket.getInetAddress().getHostName() + "/" + theSocket.getPort() + " : "); String inputLine = br.readLine(); byte[] data = inputLine.getBytes(); //将数据封装到udp数据包中,包括字节数据,服务端的地址和端口 DatagramPacket dp = new DatagramPacket(data, data.length, theSocket.getInetAddress(), theSocket.getPort()); //通过UDP套接字连接到服务端 theSocket.send(dp); //如果输入为over则结束 if (UDPEchoClient.END_FLAG.equals(inputLine)) { break; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("EchoSenderThread end : " + theSocket.getInetAddress()); } //获取UDP Socket的方法 public DatagramSocket getTheSocket() { return theSocket; } public void setTheSocket(DatagramSocket theSocket) { this.theSocket = theSocket; } } //客户端的接受线程,用于接受服务端发送的Echo信息并且显示 class EchoReceiverThread extends Thread { DatagramSocket theSocket; public EchoReceiverThread(DatagramSocket theSocket) { this.theSocket = theSocket; } @Override public void run() { //初始化接受的数据包,长度设置为最大的64KB byte[] data = new byte[65536]; DatagramPacket dp = new DatagramPacket(data, 0, data.length); //循环接受信息 while (true) { try { //阻塞式接受数据包 theSocket.receive(dp); //获取数据包的数据并打印 String content = new String(dp.getData(), 0, dp.getLength()); System.out.println("from " + dp.getAddress().getHostName() + "/" + dp.getPort() + " : " + content); //如果为over则退出 if (UDPEchoClient.END_FLAG.equalsIgnoreCase(content)) { break; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("EchoReceiverThread end : " + theSocket.getInetAddress()); } }
以下为服务端程序:
public class UDPEchoServer { //默认的主机地址 public static String DEFAULT_HOSTNAME = "localhost"; //默认的端口 public static int DEFAULT_PORT = 10000; //用于连接的UDP Socket private DatagramSocket serverSocket; public UDPEchoServer() throws SocketException { //将UDP连接绑定到默认的主机以及端口 serverSocket = new DatagramSocket(new InetSocketAddress(DEFAULT_HOSTNAME, DEFAULT_PORT)); } public static void main(String[] args) throws SocketException { //创建服务端的对象 UDPEchoServer server = new UDPEchoServer(); byte[] data = new byte[65535]; while (true) { try { //初始化接受数据包,此处缓冲区使用最大的64KB DatagramPacket dp = new DatagramPacket(data, 0, data.length); //阻塞接受数据包 server.getServerSocket().receive(dp); System.out.println("receive a datagram packet"); //打印数据包内容 System.out.println("from hostname : " + dp.getAddress() + ", port : " + dp.getPort() + " : " + new String(dp.getData(), 0, dp.getLength())); //将数据包传递给处理线程处理,提供服务器的处理效率 Thread echoHandlerThread = new EchoHandlerThread(dp, server.getServerSocket()); echoHandlerThread.start(); //放弃,让处理线程优先执行一下 Thread.yield(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public DatagramSocket getServerSocket() { return serverSocket; } public void setServerSocket(DatagramSocket serverSocket) { this.serverSocket = serverSocket; } } class EchoHandlerThread extends Thread { private DatagramPacket dp; private DatagramSocket serverSocket; public EchoHandlerThread(DatagramPacket dp, DatagramSocket serverSocket) { this.dp = dp; this.serverSocket = serverSocket; } @Override public void run() { //获取服务端接受的数据并且处理用于回显操作 String replyString = new String(dp.getData(), 0, dp.getLength()).toUpperCase(); byte[] replyData = replyString.getBytes(); //初始化数据包,包括填充数据,地址和端口 DatagramPacket ndp = new DatagramPacket(replyData, 0, replyData.length, dp.getAddress(), dp.getPort()); System.out.println("sent to hostname : " + ndp.getAddress() + ", port : " + ndp.getPort() + " : " + new String(ndp.getData(), 0, dp.getLength())); try { //通过UDP Socket发送数据包 serverSocket.send(ndp); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
测试结果:
分别打开服务端和客户端,客户端输入信息。
以下为客户端显示结果:
to localhost/10000 : hello
to localhost/10000 : from 127.0.0.1/10000 : HELLO
world
to localhost/10000 : from 127.0.0.1/10000 : WORLD
test
to localhost/10000 : from 127.0.0.1/10000 : TEST
over
EchoSenderThread end : localhost/127.0.0.1
from 127.0.0.1/10000 : OVER
EchoReceiverThread end : localhost/127.0.0.1
以下为服务端显示结果:
receive a datagram packet
from hostname : /127.0.0.1, port : 50730 : hello
sent to hostname : /127.0.0.1, port : 50730 : HELLO
receive a datagram packet
from hostname : /127.0.0.1, port : 50730 : world
sent to hostname : /127.0.0.1, port : 50730 : WORLD
receive a datagram packet
from hostname : /127.0.0.1, port : 50730 : test
sent to hostname : /127.0.0.1, port : 50730 : TEST
receive a datagram packet
from hostname : /127.0.0.1, port : 50730 : over
sent to hostname : /127.0.0.1, port : 50730 : OVER
客户端发送信息后可以收到服务端大写的回显。
相关文章推荐
- Java网络编程实践和总结 --- 基于TCP的Socket编程之实现文件上传和下载服务
- Java网络编程实践和总结 --- 基于TCP的Socket编程之echo回显的操作
- Java网络编程——基于UDP的Socket编程
- java基于UDP协议的网络编程
- 网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程
- Java开发实践 网络编程 学习、应用、总结
- Java 网络编程基于UDP_IP协议的网络编程
- 网络编程UDP总结及实践-C语言
- Java网络编程(基于TCP和UDP的通信实现)
- java网络编程:基于UDP的网络编程
- 【Java网络编程】基于 UDP 的聊天通信
- java网络编程:基于TCP的socket编程
- JAVA基础知识之网络编程——-基于UDP协议的通信例子
- Java网络编程2(基于UDP的socket编程)
- java网络编程基础夯实07-基于TCP/UDP的Socket编程(单线程)
- Java网络编程基础(三)---基于UDP编程
- 通过培训学到的一个java的基于线程,网络编程等的文件多线程断点下载器(断点功能还在操作实践中)
- 使用Java网络编程创建基于UDP协议的Server和Client
- java 网络编程 基于TCP ,UDP的网络传输
- Java网络编程_基于UDP协议的网络编程