JDK发送接收TCP/UDP数据

本文的理论和代码摘录于《疯狂Java讲义》[url]http://book.51cto.com/art/201203/322540.htm[/url],测试部分的截图是本人增加的。

1.UDP协议和TCP协议简单对比如下。
TCP协议:可靠,传输大小无限制,但是需要连接建立时间,差错控制开销大。
UDP协议:不可靠,差错控制开销较小,传输大小限制在64KB以下,不需要建立连接。

2.使用DatagramSocket发送接收UDP数据

2.1 理论
JDK使用DatagramSocket代表UDP协议的Socket,DatagramSocket类似于码头,不维护状态,不能产生IO流,它的唯一作用就是接收和发送数据报。
而DatagramPacket代表数据报,类似于集装箱。码头的作用就是负责发送、接收集装箱,而DatagramSocket的作用则是发送、接收DatagramPacket。

使用DatagramSocket发送数据报时,DatagramSocket并不知道将该数据报发送到哪里,而是由DatagramPacket自身决定数据报的目的地。就像码头并不知道每个集装箱的目的地,码头只是将这些集装箱发送出去,而集装箱本身包含了该集装箱的目的地。

发送数据是通过DatagramSocket的send()方法实现的,send()方法根据数据报的目的地址来寻径以传送数据报。

在接收数据之前,应该生成一个DatagramPacket对象,给出接收数据的字节数组及其长度。然后调用DatagramSocket的receive()方法等待数据报的到来,receive()将一直等待(该方法会阻塞调用该方法的线程),直到收到一个数据报为止。

当接收到一个DatagramPacket对象后,如果想向该数据报的发送者"反馈"一些信息,但由于UDP协议是面向非连接的,所以接收者并不知道每个数据报由谁发送过来,但程序可以调用DatagramPacket的如下3个方法来获取发送者的IP地址和端口。
InetAddress getAddress()
int getPort()
SocketAddress getSocketAddress()

2.2 编码
下面程序使用DatagramSocket实现了Server/Client结构的网络通信。本程序的服务器端使用循环1000次来读取DatagramSocket中的数据报,每当读取到内容之后便向该数据报的发送者送回一条信息。服务器端程序代码如下。

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpServer {
public static final int PORT = 30000;
// 定义每个数据报的最大大小为4KB
private static final int DATA_LEN = 4096;
// 定义接收网络数据的字节数组
byte[] inBuff = new byte[DATA_LEN];
// 以指定字节数组创建准备接收数据的DatagramPacket对象
private DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
// 定义一个用于发送的DatagramPacket对象
private DatagramPacket outPacket;
// 定义一个字符串数组,服务器端发送该数组的元素
String[] books = new String[] { "疯狂Java讲义", "轻量级Java EE企业应用实战",
"疯狂Android讲义", "疯狂Ajax讲义" };

public void init() throws IOException {
// 创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket(PORT);

// 采用循环接收数据
for (int i = 0; i < 1000; i++) {
// 读取Socket中的数据,读到的数据放入inPacket封装的数组里
socket.receive(inPacket);
// 判断inPacket.getData()和inBuff是否是同一个数组
//getData()的API设计显得有些多余--直接访问传给 DatagramPacket构造器的字节数组实参即可,无须调用该方法
System.out.println(inBuff == inPacket.getData());
// 将接收到的内容转换成字符串后输出
System.out.println(new String(inBuff, 0, inPacket.getLength()));
// 从字符串数组中取出一个元素作为发送数据
byte[] sendData = books[i % 4].getBytes();
// 以指定的字节数组作为发送数据,以刚接收到的DatagramPacket的
// 源SocketAddress作为目标SocketAddress创建DatagramPacket
outPacket = new DatagramPacket(sendData, sendData.length,
inPacket.getSocketAddress());
// 发送数据
socket.send(outPacket);
}
}

public static void main(String[] args) throws IOException {
new UdpServer().init();
}
}


客户端采用循环不断地读取用户键盘输入,每当读取到用户输入的内容后就将该内容封装成DatagramPacket数据报,再将该数据报发送出去;接着把DatagramSocket中的数据读入接收用的DatagramPacket中(实际上是读入该DatagramPacket所封装的字节数组中)。客户端程序代码如下。

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class UdpClient {
// 定义发送数据报的目的地
public static final int DEST_PORT = 30000;
public static final String DEST_IP = "127.0.0.1";
// 定义每个数据报的最大大小为4KB
private static final int DATA_LEN = 4096;
// 定义接收网络数据的字节数组
byte[] inBuff = new byte[DATA_LEN];
// 以指定的字节数组创建准备接收数据的DatagramPacket对象
private DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
// 定义一个用于发送的DatagramPacket对象
private DatagramPacket outPacket = null;

public void init() throws IOException {
// 创建一个客户端DatagramSocket,使用随机端口
DatagramSocket socket = new DatagramSocket();
// 初始化发送用的DatagramSocket,它包含一个长度为0的字节数组
outPacket = new DatagramPacket(new byte[0], 0,
InetAddress.getByName(DEST_IP), DEST_PORT);
// 创建键盘输入流
Scanner scan = new Scanner(System.in);
// 不断地读取键盘输入
while (scan.hasNextLine()) {
// 将键盘输入的一行字符串转换成字节数组
byte[] buff = scan.nextLine().getBytes();
// 设置发送用的DatagramPacket中的字节数据
outPacket.setData(buff);
// 发送数据报
socket.send(outPacket);
// 读取Socket中的数据,读到的数据放在inPacket所封装的字节数组中
socket.receive(inPacket);
System.out.println(new String(inBuff, 0, inPacket.getLength()));
}
}

public static void main(String[] args) throws IOException {
new UdpClient().init();
}
}


2.3 测试
下面我们测试一下,打开UdpServer,然后打开2个UdpClient。随便哪个Client都可以发消息给Server,同时会Server的回复。
用[url=http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx]Process Explorer[/url]监控一下端口可以发现,Server开启了30000端口,而Client则开启了随机端口,这次Client1是49509,Client2是52535。
服务器和Client1、Client2都是没有连接的。
[img]http://dl2.iteye.com/upload/attachment/0096/0395/db85056b-1504-35c6-ae84-3c20195bf477.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0096/0401/e2aa3205-dac3-3027-911a-00abc7773194.png[/img]

3.使用Socket发送接收TCP数据
对比之下,如果是TCP的话,就是一般的Socket和ServerSocket互通。
服务端

import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server
{
public static void main(String[] args)
throws IOException
{
// 创建一个ServerSocket,用于监听客户端Socket的连接请求
ServerSocket ss = new ServerSocket(30000);
// 采用循环不断接受来自客户端的请求
while (true) {
// 每当接受到客户端Socket的请求,服务器端也对应产生一个Socket,并开启一个新线程负责和客户端通信
final Socket s = ss.accept();
new Thread(new Runnable(){

@Override
public void run() {
// 将Socket对应的输出流包装成PrintStream
PrintStream ps=null;
try {
ps = new PrintStream(s.getOutputStream());
int i=9;
//此处死循环是为了观察端口,不然下面的ps一关闭,就看不到端口情况了
while (i==9) {
ps.println("您好,您收到了服务器的新年祝福!");
}
ps.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}

}

}
)
.start();
}
}
}


客户端

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Socket;

public class Client
{
public static void main(String[] args)
throws Exception
{
Socket socket = new Socket("127.0.0.1" , 30000);
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
int i=9;
while (i==9) {
String line = br.readLine();
System.out.println("来自服务器的数据:" + line);
}
br.close();
socket.close();
}
}


我们也可以做个试验,开启1个Server,2个Client。
从图中可以发现,Server开启了30000端口,并且和Client的53170和53171建立了连接。同时Server的30000端口还在监听准备接受新的Client的到来。
[img]http://dl2.iteye.com/upload/attachment/0096/0393/50daaa78-4d4a-3e6f-86d6-e9e678548331.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0096/0399/d715c6de-edd8-3099-80f1-20350645e396.png[/img]


我们做另一个测试,同时开启UdpServer,UdpClient,并同时开启(TCP)Server,Client,发现他们不会冲突,都正常工作呢。
[img]http://dl2.iteye.com/upload/attachment/0096/0397/ce6f7f90-f18a-3cfb-8c2a-a339f9f7a12d.png[/img]

由此可以得到另外一个结论:TCP和UDP端口是独立的,他们之间没有关系。TCP可以有65536个端口,UDP同样也有65536个端口可用。这样一台计算机上总共就有131072个端口可用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值