[学习笔记]Java网络编程之UDP通讯

计算机网络体系结构

1. 体系结构

计算机网络各层及其协议的集合,称为网络的体系结构。目前公认的计算机网络体系结构有概念清楚理论完善的OSI七层协议结构和实际上广泛应用的TCP/IP四层体系结构。



2. 各层概述

  • 应用层:直接为用户的程序提供服务,如支持万维网应用的HTTP协议,支持电子邮件的SMTP协议等。
  • 运输层:负责向两个主机中进程之间的通信提供服务,主要使用面向连接的TCP协议和无连接的UDP协议。
  • 网络层:负责为分组交换网上不同主机提供地址交付的通信服务,该层使用IP协议,所以在该层传送的数据称之为IP数据报。路由器工作在该层。
  • 数据链路层:负责将IP数据报在相邻的节点之间传输,该层将IP数据报封装成帧,将IP地址转换为MAC硬件地址封装在帧中进行节点间的传输。网桥,桥接器和交换机都工作在该层。
  • 物理层:包括数据传输的介质和电气特性,包括电缆,光缆,光信号,电信号的标准等。转发器,信号放大器都工作在该层。
  • 每一层的实际运作对上一层都是透明的。
  • 应用程序间通过网络交换数据,数据在发送方从上到下层层封装,通过物理层传输,到接收方又从下至上层层拆封还原原始数据。

Java中的网络通讯

1. IP地址

1.1 概述

IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。

1.2 域名解析

为了方便记忆,出现域名,并且需要从域名转化为IP地址的协议或服务,该服务就是域名解析(DNS)。

特殊地址
  • 127.0.0.1:本机回环地址。
  • 主机号为全1:该网络广播地址。

1.3 InetAddress类

概述
  • 该类没有构造器。
  • 该类用于IP地址的封装。

方法
  • 返回本机IP原始地址
    byte[]  getAddress()
  • 通过主机名或IP地址获取IP地址对象
    static InetAddress[]  getAllByName(String host)
    static InetAddress  getByAddress(byte[] addr)
    static InetAddress  getByAddress(String host, byte[] addr)
    static InetAddress  getByName(String host)
  • 获取主机IP地址或主机名
  • static InetAddress  getLocalHost()
    String  getHostAddress()
    String  getHostName()
示例
  
  
    
    
package net;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetDemo {
public static void main(String[] args) throws UnknownHostException {
// 获取本机IP对象
InetAddress iphost = InetAddress. getLocalHost();
String ipstr = iphost.getHostAddress(); // "192.168.1.22"
String ipname = iphost.getHostName(); // "YH-PC"
// 获取指定IP对象
InetAddress ip = InetAddress.getByName("192.168.1.1");
ipstr = ip.getHostAddress(); // "192.168.1.1"
ipname = ip.getHostName(); // "192.168.1.1"
// 获取指定主机的IP对象
InetAddress[] ips = InetAddress.getAllByName("www.google.com.hk");
for (int i = 0; i < ips.length; i++) {
ipstr = ips[i].getHostAddress();
ipname = ips[i].getHostName();
System.out.println( ipstr);
System.out.println( ipname);
// 173.194.120.88
// www.google.com.hk
// 216.239.32.39
// www.google.com.hk
}
}
}

2. 端口号

  • 全称为TCP/IP端口,是应用层用于识别通讯进程的接口。
  • 有效端口:0~65535,系统使用或保留的端口是:0~1024。

3. 传输层协议

3.1 UDP协议

  • 无连接的,不保证可靠交付,只能提供尽最大努力交付。
  • 无需创建链接,速度较快。
  • 支持一对一,一对多,多对一,多对多通讯。
  • 面向报文的通信。
  • 没有拥塞控制,所以可能会有丢包,但是传输速率不受影响,延时较小,适合实时性要求高的网络视频,聊天等。

3.2 TCP协议

  • 面向连接的,提供可靠交付服务。
  • TCP链接只能有两个端点,所以只能是点对点(一对一)通讯。
  • TCP提供全双工通信,适合大数据量的传输。
  • 面向字节流的通信。
  • 需要通过三次握手建立链接,速度稍慢。

4. 通讯过程

  • 明确IP地址。
  • 明确通讯端口。
  • 明确使用协议。
  • 建立Socket(套接字,类似于通信链接的端口,插口)通信服务。
  • 建立通信数据报文Package。
  • 使用Socket来发送或接收报文。

UDP协议相关API

1. DatagramSocket类

概述

该类用于UDP通讯套接字的建立。

构造器

  • 绑定本地主机创建套接字
    DatagramSocket()
    DatagramSocket(int port)
  • 绑定指定主机创建套接字(当一台机器拥有多于一个IP地址的时候,由它创建的实例仅仅接收来自指定地址的报文)
    DatagramSocket(DatagramSocketImpl impl)
    DatagramSocket(int port, InetAddress laddr)
    DatagramSocket(SocketAddress bindaddr)

常用方法

  • 将套接字绑定到特定的IP地址和端口号
    void  bind(SocketAddress addr)
  • 关闭该套接字
    void  close()
  • 将该套接字连接至远程地址(用于建立单向通讯)
    void  connect(InetAddress address, int port)
    void  connect(SocketAddress addr)
  • 断开链接
    void  disconnect()
  • 获取与该套接字连接的地址
    InetAddress  getInetAddress()
    SocketAddress  getRemoteSocketAddress()
  • 获取与该套接字连接的端口号
    int  getPort()
  • 获取与该套接字绑定的本地地址
    InetAddress  getLocalAddress()
    SocketAddress  getLocalSocketAddress()
  • 获取与该套接字绑定的本地端口号
    int  getLocalPort()
  • 判断套接字状态
    boolean  isBound()
    boolean  isClosed()
    boolean  isConnected()
  • 接收和发送
    void  receive(DatagramPacket p)
    void  send(DatagramPacket p)
  • 设置接收和发送缓冲区大小以及超时限制
    void  setReceiveBufferSize(int size)
    void  setSendBufferSize(int size)
    void  setSoTimeout(int timeout)

2. DatagramPacket类

概述

该类用于数据报文的建立。

构造器

  • 建立发送报文
    DatagramPacket(byte[] buf, int length, InetAddress address, int port)
    DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
    DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)
    DatagramPacket(byte[] buf, int length, SocketAddress address)
  • 建立接收报文
    DatagramPacket(byte[] buf, int length)
    DatagramPacket(byte[] buf, int offset, int length)

常用方法

  • 获取该数据报发送的目的地址或接收的源地址。
    InetAddress  getAddress()
    SocketAddress  getSocketAddress()
    int  getPort()
  • 获取数据报数据长度和偏移量
    int  getLength()
    int  getOffset()
  • 设置数据报发送地址
    void  setAddress(InetAddress iaddr)
    void  setSocketAddress(SocketAddress address)
    void  setPort(int iport)
  • 设置数据报数据
    void  setData(byte[] buf)
    void  setData(byte[] buf, int offset, int length)
    void  setLength(int length)

3. 通信步骤

发送端

  • 建立套接字。
  • 明确目标地址和端口号,建立带地址的数据报文。
  • 使用套接字发送数据报文。
  • 关闭套接字。

接收端

  • 明确端口号,建立带端口号套接字。
  • 建立接收的数据报文。
  • 使用套接字接收数据报文。
  • 关闭套接字。

4. 示例

UDP发送端

   
   
package net;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDP_Send {
public static void main(String[] args) throws IOException {
/*
* UDP发送端
*/
System.out.println( "UDP发送端");
// 获取本地IP地址
InetAddress ip = InetAddress.getLocalHost();
System.out.println( "Destination IP = " + ip.getHostAddress());
// 1. 建立UDP的socket
DatagramSocket ds = new DatagramSocket();
// 2. 将数据封装到数据包中
byte[] buf = "hello UDP!".getBytes();
DatagramPacket dp = new DatagramPacket( buf, buf.length , ip , 22222);
// 3. 使用Socket对象的send方法将数据包发送出去
ds.send(dp);
// 4. 关闭资源。
ds.close();
}
}


UDP接收端

   
   
package net;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDP_Receive {
public static void main(String[] args) throws IOException {
/*
* UDP接收端
*/
System.out.println( "UDP接收端");
// 1. 建立UDP的socket
DatagramSocket ds = new DatagramSocket(22222);
// 2. 定义数据包方便接收数据
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket( buf, buf.length );
// 3. 使用Socket对象的receive方法阻塞接收数据
ds.receive(dp);
// 4. 获取接收到的数据
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String data = new String( dp.getData(), 0, dp.getLength());
System.out.println( "ip = " + ip + "\nport = " + port + "\ndata = " + data );
// 4. 关闭资源。
ds.close();
}
}


实例

聊天室程序

   
   
package net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class Test_ChatByUDP {
public static void main(String[] args) throws SocketException, UnknownHostException {
// 创建用于发送和接收的套接字,端口33333只用于聊天室接收程序。
DatagramSocket sendDs = new DatagramSocket();
DatagramSocket receDs = new DatagramSocket(33333);
// 创建广播地址
InetAddress ip = InetAddress.getByName("192.168.1.255");
// 创建发送和接收对象
Send sd = new Send( sendDs, ip);
Rece rc = new Rece( receDs);
new Thread( sd).start();
new Thread( rc).start();
}
}
class Send implements Runnable {
private DatagramSocket ds;
private InetAddress ip;
public Send(DatagramSocket ds, InetAddress ip) {
super();
this.ds = ds;
this.ip = ip;
}
@Override
public void run() {
// 读取键盘录入
BufferedReader br = new BufferedReader( new InputStreamReader(System.in ));
String str = null;
try {
while (( str = br.readLine()) != null) {
// 发送键盘录入的字符
DatagramPacket dp = new DatagramPacket(str.getBytes(), str .length(), ip , 33333);
ds.send( dp);
if ( "exit".equals(str )) {
break;
} else if ( "close".equals(str )) {
break;
}
}
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Rece implements Runnable {
private DatagramSocket ds;
public Rece(DatagramSocket ds) {
super();
this.ds = ds;
}
@Override
public void run() {
while ( true) {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket( buf, buf.length );
try {
// 阻塞接收
ds.receive( dp);
} catch (IOException e) {
e.printStackTrace();
}
// 获取发送方地址
String ip = dp.getAddress().getHostAddress();
String str = new String( dp.getData(), 0, dp.getLength());
int port = dp.getPort();
if ("exit".equals( str)) {
// 判断发送字符
System. out.println(ip + ":" + port + " 离开了聊天室!" );
} else if ("close".equals( str)) {
System. out.println(ip + ":" + port + " 请求关闭聊天室!" );
break;
} else {
System. out.println(ip + ":" + port + " 说:" + str);
}
}
ds.close();
System.out.println( "聊天室已关闭!" );
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值