android基于UDP实现聊天小功能
一、DatagramSocket
DatagramSocket 是 Java 中用于发送和接收 UDP 数据包的类。它提供了创建和管理 UDP 套接字的功能。以下是 DatagramSocket 类的一些常用方法:
-
DatagramSocket(): 创建一个未绑定到特定本地地址和端口的 DatagramSocket 实例。
-
DatagramSocket(int port): 创建一个绑定到指定本地端口的 DatagramSocket 实例。
-
void send(DatagramPacket packet): 发送一个数据包到目标地址。
-
void receive(DatagramPacket packet): 接收一个数据包。
-
void bind(SocketAddress address): 将套接字绑定到指定的本地地址。
-
void close(): 关闭套接字。
二、DatagramPacket
DatagramPacket 是 Java 中用于表示 UDP 数据包的类。它包含了数据包的内容、目标地址和端口等相关信息。以下是 DatagramPacket 类的一些常用方法:
-
DatagramPacket(byte[] data, int length): 创建一个接收数据包的实例,使用指定的字节数组和数据长度。
-
DatagramPacket(byte[] data, int length, InetAddress address, int port): 创建一个发送数据包的实例,使用指定的字节数组、数据长度、目标地址和目标端口。
-
byte[] getData(): 获取接收到的数据包的字节数组。
-
void setData(byte[] data): 设置要发送的数据包的字节数组。
-
int getLength(): 获取数据包的长度。
-
void setLength(int length): 设置数据包的长度。
-
InetAddress getAddress(): 获取数据包的目标地址。
-
void setAddress(InetAddress address): 设置数据包的目标地址。
-
int getPort(): 获取数据包的目标端口。
-
void setPort(int port): 设置数据包的目标端口。
三、核心代码
3.1、接收UDP数据线程MyReceiveThread
private class MyReceiveThread implements Runnable {
@Override
public void run() {
//要循环接收数据
while (true) {
try {
Log.d(TAG, "udp waiting....");
receivedSocket.receive(packet);
//获取数据的时候,要使用带有offset的
String msg = new String(packet.getData(), 0, packet.getLength(), "UTF-8");
Log.e(TAG, "Received msg:" + msg);
targetIp = packet.getAddress().getHostAddress();
MessageEntity messageEntity = new MessageEntity();
messageEntity.setRole(MessageEntity.ROLE_CLIENT);
messageEntity.setMessage(msg);
mEntities.add(messageEntity);
etTargetIp.post(() -> mMessageAdapter.notifyDataSetChanged());
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
receivedSocket.close();
}
}
}
}
- 接收数据的时候,要开启一个循环
- 获取数据的时候,要使用String(byte bytes[], int offset, int length, String charsetName)
3.2、发送数据
public void sendMsg(View view) {
targetIp = etTargetIp.getText().toString();
new Thread(new Runnable() {
@Override
public void run() {
try {
DatagramSocket socket = new DatagramSocket();
String message = msg.getText().toString();
byte[] msgs = message.getBytes("UTF-8");
InetAddress address = InetAddress.getByName(targetIp);
DatagramPacket packet = new DatagramPacket(msgs, msgs.length);
packet.setAddress(address);
packet.setPort(PORT);
socket.send(packet);
MessageEntity messageEntity = new MessageEntity();
messageEntity.setRole(MessageEntity.ROLE_HOST);
messageEntity.setMessage(message);
mEntities.add(messageEntity);
etTargetIp.post(() -> mMessageAdapter.notifyDataSetChanged());
} catch (SocketException e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
} catch (UnknownHostException e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
}
}
}).start();
}
Github
四、使用tcpdump抓包
4.1、tcpdump官网下载
4.2、adb push
adb push D:\tcpdump /data/
4.3、给tcpdump执行权限
adb shell
cd data
chmod +x tcpdump
4.3、查看设备的网口
./tcpdump -D
4.4、抓取wlan0的数据包
./tcpdump -i wlan0 -w 2.pcap
4.5、导出抓包数据
adb pull /data/2.pcap D:\
4.6、使用wireshark打开2.pcap文件
五、网络协议
上面是一条完整的UDP数据包
5.1、以太网(Ethernet)
以太网(Ethernet)是一种常用的有线局域网(LAN)技术,用于在计算机和其他网络设备之间进行数据通信。它定义了物理层和数据链路层的规范,用于在局域网中传输数据帧。
以太网使用数据链路层协议来组织和管理数据的传输。最常见的以太网数据链路层协议是**以太网帧(Ethernet Frame)**格式,它定义了数据帧的结构和字段。以太网帧包括源和目标MAC地址、帧类型、数据、校验和等字段。
5.1.1、以太网首部结构
- 以太网首部的长度为14个字节
- 以太网首部的字段包括:
-
目标MAC地址(Destination MAC Address)6个字节:指示帧的目标设备的MAC地址,用于将帧发送到正确的目标设备。
-
源MAC地址(Source MAC Address)6个字节:指示帧的发送方设备的MAC地址,用于标识帧的来源。
-
以太网类型(Ethernet Type)或长度(Length)2个字节:指示帧中包含的上层协议类型或数据部分长度。以太网类型字段通常用于标识上层协议,如IPv4(值为0x0800)、IPv6(值为0x86DD) 和 ARP(值为0x0806) 等。长度字段在某些情况下用于指示数据部分的长度。
-
5.2、IP协议
IP(Internet Protocol)协议是互联网中最核心的网络协议之一,它负责在网络中传输数据包。IP协议定义了数据包的格式、路由选择和分组交换等机制,使得数据能够在不同的网络之间进行传输和路由。
以下是IP协议的主要特点和功能:
1、 无连接性:IP协议是一种无连接的协议,每个IP数据包都是独立传输的。在发送数据时,每个数据包被独立处理,并根据目标地址进行路由选择。这种无连接性使得IP协议的传输效率较高,但也意味着它不提供可靠性保证,不保证数据包的顺序和可靠传输。
2、数据包格式:IP协议定义了数据包的格式,通常称为IP数据报(IP Datagram)。IP数据报由首部(Header)和有效载荷(Payload)组成。IP首部包含了源IP地址、目标IP地址、协议版本、首部长度、服务类型、数据包长度、标识、生存时间(TTL)、校验和等字段,用于控制和识别数据包。有效载荷部分包含上层协议的数据。
3、IP地址:IP协议使用IP地址来标识网络中的设备。IP地址是一个32位(IPv4)或128位(IPv6)的数字标识符。IPv4地址由四个8位的数字组成,每个数字用点分隔。IPv6地址通常使用八组四位的十六进制数表示。IP地址用于寻址和路由选择,确保数据包能够正确到达目标设备。
4、路由选择:IP协议使用路由选择算法将数据包从源主机传输到目标主机。路由选择根据目标IP地址和路由表中的路由信息进行决策,确定下一跳的路径。路由选择算法的目标是找到最佳路径,使数据包能够以最高效的方式到达目标设备。
5、分组交换:IP协议采用分组交换的方式传输数据包。数据包在发送端被分成较小的数据块(分组),每个分组独立传输。在网络中,每个分组根据目标地址进行独立的路由选择和转发,最终在目标设备上重新组装成完整的数据包。这种分组交换的方式提高了网络的利用率和灵活性。
6、差错检测:IP协议使用校验和字段对数据包进行差错检测。发送端在发送数据包时计算校验和,并将其包含在IP首部中。接收端在接收到数据包后,重新计算校验和,并与接收到的校验和进行比较,以检测数据包是否在传输过程中发生了错误。
5.2.1、IP数据报首部
IPv4首部字段(20字节):
-
版本(Version):占4位,指定IP协议的版本号,IPv4为4。
-
首部长度(Header Length):占4位,指定IP首部的长度,以4字节为单位。由于IPv4首部长度最多为15个4字节(60字节),因此它实际上是首部中32位字的数量。
16进制45的二进制为0100 0101,其中前4位代表IP协议的version,后4位代表IP协议首部长度 -
服务类型(Type of Service):占8位,用于指定数据包的服务质量要求,如优先级、延迟和吞吐量等。
-
总长度(Total Length):占16位,指定整个IP数据包的长度,包括首部和数据部分。
-
标识(Identification):占16位,用于标识数据包的唯一性,通常由发送端设置,并在接收端进行处理。
-
标志(Flags):占3位,用于指示数据包的特殊处理标志。其中包括禁止分片标志(DF)和更多分片标志(MF)。
-
片偏移(Fragment Offset):占13位,用于指定数据包片段在原始数据包中的位置。
-
生存时间(Time to Live,TTL):占8位,表示数据包在网络中可以经过的最大路由跳数。每经过一个路由器,该字段的值减1,当值为0时,数据包将被丢弃。
-
协议(Protocol):占8位,指定上层协议类型,如TCP、UDP或ICMP。
-
首部校验和(Header Checksum):占16位,用于校验IP首部的完整性。
-
源IP地址(Source IP Address):占32位,指定数据包的源IP地址。
-
目标IP地址(Destination IP Address):占32位,指定数据包的目标IP地址。
5.3、UDP协议
UDP(User Datagram Protocol)是一种面向无连接的传输层协议。与TCP(Transmission Control Protocol)相比,UDP更加轻量级,没有建立和维护连接的开销,因此传输效率更高,但也不提供可靠性保证。
以下是UDP的主要特点和用途:
-
无连接性:UDP是一种无连接的协议,不需要在发送数据之前建立连接。每个UDP数据包都是独立发送的,不会进行握手和确认。这使得UDP的传输速度较快,适用于实时性要求较高的应用。
-
简单性与轻量性:相比TCP,UDP的协议头部较小,仅包含源端口、目标端口、长度和校验和等简单字段。这使得UDP的开销较小,传输效率较高。
-
不可靠性:UDP不提供数据包的可靠性保证。它不保证数据包的顺序和可靠传输,也不进行重传和拥塞控制。因此,如果使用UDP进行数据传输,必须由应用层自行处理数据的完整性和可靠性。
-
广播和多播支持:UDP支持广播和多播传输。通过UDP广播,可以将数据包发送到同一网络中的所有主机。而通过UDP多播,可以将数据包发送到特定的多播组。
-
实时性要求:由于UDP的无连接性和较低的开销,它在实时性要求较高的应用中得到广泛应用。例如,音频和视频流传输、实时游戏和语音通信等。
-
域名解析:UDP常用于域名解析中的DNS(Domain Name System)服务。DNS使用UDP协议在域名服务器和客户端之间进行域名解析查询和响应。
5.3.1、UDP首部
UDP(User Datagram Protocol)的首部(Header)是UDP数据报中用于控制和识别的固定长度字段集合。UDP首部包含以下字段:
-
源端口号(Source Port):占16位,指定发送方的端口号。
-
目标端口号(Destination Port):占16位,指定接收方的端口号。
-
长度(Length):占16位,指定UDP数据报的总长度(包括首部和数据部分)。
-
校验和(Checksum):占16位,用于校验UDP数据报的完整性。
5.4、ARP协议
ARP(Address Resolution Protocol)是一种用于将IP地址映射到物理MAC地址的协议。它在TCP/IP网络中起着重要的作用,用于解决主机在发送数据时,如何根据目标IP地址找到对应的物理MAC地址的问题。
以下是ARP协议的基本工作原理:
-
ARP请求:当主机需要向目标IP地址发送数据时,首先检查本地的ARP缓存(ARP Cache)中是否存在目标IP地址对应的MAC地址。如果缓存中没有对应的映射关系,主机会发送一个ARP请求广播到本地网络中的所有主机。
-
ARP应答:接收到ARP请求的主机,如果发现自己的IP地址与请求中的目标IP地址匹配,就会发送一个ARP应答给请求方,包含自己的MAC地址。
-
ARP缓存更新:请求方收到ARP应答后,会将目标IP地址与对应的MAC地址的映射关系存储在本地的ARP缓存中,以便将来使用。这样,当再次需要向目标IP地址发送数据时,就可以直接从ARP缓存中获取目标的MAC地址。
ARP协议使用的是广播的方式,因此ARP请求和应答都会发送到本地网络中的所有主机。只有与请求IP地址匹配的主机才会应答,其他主机会忽略。
ARP协议的作用是解决逻辑地址(IP地址)到物理地址(MAC地址)的映射问题,使数据能够正确地在网络中传输。它在局域网中起着重要的作用,帮助主机建立和维护IP地址与MAC地址之间的映射关系。
5.4.1、ARP协议的首部
ARP(Address Resolution Protocol)协议的首部(Header)包含以下字段:
-
硬件类型(Hardware Type):占2个字节,指定网络硬件类型,如以太网、无线局域网等。常见的以太网类型为1。
-
协议类型(Protocol Type):占2个字节,指定网络层协议类型,通常为IPv4或IPv6。IPv4的协议类型为0x0800。
-
硬件地址长度(Hardware Address Length):占1个字节,指定硬件地址的长度,通常为6个字节(以太网MAC地址长度)。
-
协议地址长度(Protocol Address Length):占1个字节,指定协议地址的长度,通常为4个字节(IPv4地址长度)。
-
操作码(Operation Code):占2个字节,指定ARP消息的类型。常见的操作码包括请求(1)和应答(2)。
-
发送方硬件地址(Sender Hardware Address):占长度与硬件地址长度字段指定的长度相同,指定发送方的硬件地址,即发送方的MAC地址。
-
发送方协议地址(Sender Protocol Address):占长度与协议地址长度字段指定的长度相同,指定发送方的协议地址,即发送方的IP地址。
-
目标硬件地址(Target Hardware Address):占长度与硬件地址长度字段指定的长度相同,指定目标的硬件地址,即目标的MAC地址。
因为目前还不知道对方的Mac地址,所以此时为0 -
目标协议地址(Target Protocol Address):占长度与协议地址长度字段指定的长度相同,指定目标的协议地址,即目标的IP地址。
ARP协议首部的长度固定为28个字节(224位)。
使用ARP协议,主机可以通过发送ARP请求来查询目标IP地址对应的MAC地址。接收到ARP请求的主机会发送ARP应答,将自己的MAC地址告知请求方。这样,发送方就能够获得目标IP地址与对应的MAC地址的映射关系,并在数据传输时使用正确的MAC地址。
需要注意的是,ARP协议在广播域内工作,即只适用于同一局域网内的主机之间。在跨网段通信时,会使用其他协议(如ARP的扩展协议Proxy ARP、ARP的替代协议NDP等)来解决IP地址到MAC地址的映射问题。