目录
一、介绍
UDP(User Datagram Protocol)是一种无连接的传输协议,它在网络通信中常用于实时应用程序,例如音频、视频流传输和在线游戏。与TCP(Transmission Control Protocol)相比,UDP具有更低的开销和更高的传输速度,但不提供可靠性和错误检测功能。
网络通信的三要素:
(1)、IP地址:
IP地址(Internet Protocol Address)是用于在网络中标识和定位设备的一组数字。它是互联网通信所必需的基本元素之一,用于确定数据包在网络中的传输路径。
IP地址的作用是为每个连接到互联网的设备提供一个唯一的标识符,类似于世界上每个家庭都有一个不同的地址来进行邮件投递。通过IP地址,计算机可以相互识别并建立网络通信。
IP地址分为两部分:网络部分和主机部分。网络部分标识了设备所连接的网络,而主机部分标识了网络中的具体设备。
IP地址由32位或128位二进制数字组成,在常见的使用中通常表示为分组的四个十进制数,例如:192.168.0.1。IPv4(Internet Protocol version 4)地址使用32位,而IPv6(Internet Protocol version 6)地址使用128位,以满足互联网设备的增长需求。
IPv4:
IPv6:
IP地址形式:
- 公网地址、和私有地址(局域网使用)。
- 192.168. 开头的就是常见的局域网地址,范围即为192.168.0.0--192.168.255.255,专门为组织机构内部使用。
IP常用命令:
- ipconfig:查看本机IP地址
- ping IP地址:检查网络是否连通
特殊IP地址:
- 本机IP: 127.0.0.1或者localhost:称为回送地址也可称本地回环地址,只会寻找当前所在本机。
IP地址操作类:
- InetAddress (在下面)
(2)、端口:
端口(Port)是用于在计算机网络中标识应用程序或服务的数字。它是网络通信中的一个重要概念,用于区分不同的网络应用和服务。
在互联网协议(IP)中,每个主机都有一个唯一的IP地址,而端口号则用于标识该主机上特定的应用程序或服务。它可以看作是一个门,通过该门可以与特定的应用程序进行交流。
端口号是一个16位的整数,范围从0到65535。其中,0到1023的端口号被称为“知名端口”(Well-known Ports),用于标识一些常见的服务,如HTTP(端口号80)、FTP(端口号21)等。1024到49151之间的端口号被称为“注册端口”(Registered Ports),用于一些已经注册的应用程序。49152到65535之间的端口号被称为“动态或私有端口”(Dynamic or Private Ports),通常由操作系统动态分配给客户端应用程序。
通过使用源IP地址、目标IP地址和端口号,可以唯一确定网络中的通信路径。发送方将数据发送到目标主机的特定端口,接收方根据目标端口来接收和处理数据。
端口的作用是实现多个应用程序同时在同一台设备上进行网络通信,使得网络上的数据可以正确地路由到目标应用程序。通过将数据包与特定的端口相关联,网络设备可以将数据传递给正确的应用程序,并实现应用层之间的通信。
总结而言,端口是在计算机网络中用于标识不同应用程序或服务的数字,它通过与IP地址一起使用,帮助网络设备正确地路由和交付数据。
注意:我们自己开发的程序选择注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。
(3)、协议:
协议(Protocol)是在计算机网络中定义数据通信规则和标准的集合。它规定了网络中各个设备之间如何进行通信、数据格式的结构、错误处理等细节,确保信息能够按照特定的方式传输和解释。
在计算机网络中,不同的设备和应用程序需要遵循相同的协议来进行有效的通信。协议定义了数据交换的规则和语义,使得通信双方能够理解和解释彼此发送和接收的数据。
常见的网络协议有很多,其中一些重要的包括:
-
TCP/IP协议:TCP/IP(Transmission Control Protocol/Internet Protocol)是互联网最基本的协议族,它定义了数据在网络中的传输方式和规则,包括IP地址分配、数据路由、数据分片、错误检测和纠错等。TCP/IP协议族包括了多个协议,如IP协议、TCP协议、UDP协议等。
-
HTTP协议:HTTP(Hypertext Transfer Protocol)是一种应用层协议,用于在Web浏览器和Web服务器之间传输超文本数据。它定义了浏览器发送请求和服务器响应请求的格式和规则,是Web应用程序最常用的协议之一。
-
FTP协议:FTP(File Transfer Protocol)是用于在网络上进行文件传输的协议。它定义了客户端和服务器之间进行文件传输的规则,包括建立连接、身份验证、文件上传和下载等。
-
SMTP协议:SMTP(Simple Mail Transfer Protocol)是用于电子邮件传输的协议。它规定了电子邮件的传输方式、信封格式、消息格式等,确保电子邮件能够从发送方传递到接收方。
-
DNS协议:DNS(Domain Name System)是将域名转换为IP地址的协议。它提供了一个分布式的命名系统,将人类可读的域名映射到计算机可识别的IP地址,实现了域名解析的功能。
这些只是众多网络协议中的一小部分,每个协议都有不同的功能和应用领域。通过遵守共同的协议标准,网络设备和应用程序可以相互通信并完成特定的任务。协议的使用使得计算机网络能够高效、可靠地运行,并实现各种功能和服务。
UDP协议:
- UDP是一种无连接、不可靠传输的协议。
- 将数据源IP、目的地IP和端口封装成数据包,不需要建立连接
- 每个数据包的大小限制在64KB内
- 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
- 可以广播发送 ,发送数据结束时无需释放资源,开销小,速度快。
UDP通信的特点:
- 无连接性:UDP是无连接的传输协议,意味着在发送数据之前不需要建立连接。每个数据包都是独立的,由源主机直接发送到目标主机。
- 速度快:UDP没有像TCP那样的握手过程和其他的控制机制,因此传输速度较快。
- 简单:相对于TCP,UDP协议更加简单,头部开销较小。
- 不可靠:UDP不提供可靠性保证,因为它不处理丢失、重复或乱序的数据包。这意味着在数据传输过程中可能存在丢包的风险。
- 适合实时应用:由于UDP的快速传输和较低的延迟,它在对实时性要求较高的应用中表现良好,如语音通话、视频流等。
二、常用类及其方法
1. DatagramSocket类
DatagramSocket
类表示一个UDP套接字,用于发送和接收数据报。它提供了与底层网络通信相关的方法。
常用方法:
DatagramSocket()
:创建一个未绑定到任何本地地址和端口的新套接字。DatagramSocket(int port)
:创建一个绑定到指定本地端口的新套接字。void send(DatagramPacket packet)
:将指定的数据包发送到目标主机。void receive(DatagramPacket packet)
:从套接字中接收数据包。
2. DatagramPacket 类
DatagramPacket
类表示一个UDP数据包,包含要发送或接收的数据以及目标主机的地址和端口号。
常用方法:
DatagramPacket(byte[] data, int length)
:使用指定的数据和长度创建一个无发送地址或端口号的数据包。DatagramPacket(byte[] data, int offset, int length)
:使用指定的数据、偏移量和长度创建一个无发送地址或端口号的数据包。DatagramPacket(byte[] data, int offset, int length, InetAddress address, int port)
:使用指定的数据、偏移量、长度、目标主机地址和端口号创建一个数据包。byte[] getData()
:获取该数据包的数据。InetAddress getAddress()
:获取该数据包的目标主机地址。int getPort()
:获取该数据包的目标端口号。
3. InetAddress类
InetAddress
类表示一个IP地址,并提供了与IP地址相关的方法。
常用方法:
- public static InetAddress getLocalHost():返回本主机的地址对象。
- public static InetAddress getByName(String host):得到指定主机的IP地址对象,参数是域名或者IP地址。
- public String getHostName():获取此IP地址的主机名。
- public String getHostAddress():返回IP地址字符串。
- public boolean isReachable(int timeout):在指定毫秒内连通该IP地址对应的主机,连通返回true。
三、udp例子
MainActivity:
package com.example.udpdemo;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
Button sendBtn, decideBtn;
EditText sendMsg, ipEd;
TextView receiveMsg;
// 对方的ip地址
String ip;
UdpTool udpTool;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
// 发送信息的函数
sendBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//在Android通信协议必须要放在线程里面进行
String str = sendMsg.getText().toString();
Log.v("发送", str);
if (ip!=null){
udpTool.sendMessage(str,ip);
}else {
Toast.makeText(MainActivity.this, "请输入对方ip和端口", Toast.LENGTH_SHORT).show();
}
}
});
decideBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ip = ipEd.getText().toString();
Toast.makeText(MainActivity.this, "确定成功!", Toast.LENGTH_SHORT).show();
}
});
// 接收信息的函数
try {
udpTool.receiveMessage(receiveMsg);;
} catch (Exception e) {
e.printStackTrace();
}
}
private void initView() {
sendBtn = findViewById(R.id.send_btn);
sendMsg = findViewById(R.id.send_message);
receiveMsg = findViewById(R.id.receive_message);
ipEd = findViewById(R.id.target_ip_ed);
decideBtn = findViewById(R.id.decide_btn);
udpTool = new UdpTool(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 释放资源
udpTool.closeSocket();
}
}
UdpTool :
package com.example.udpdemo;
import android.content.Context;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class UdpTool {
private String TAG = "UdpTool";
DatagramSocket mSocket;
Context context;
public UdpTool(Context context){
this.context = context;
}
/**
* 发送信息
* @param msg 信息内容
* @param ip 对方ip地址
*/
public void sendMessage(String msg, String ip){
// 需要开线程来发数据
new Thread(new Runnable() {
@Override
public void run() {
if (mSocket == null){
try {
// 这个是本机的端口号
mSocket = new DatagramSocket(20010);
} catch (SocketException e) {
e.printStackTrace();
}
}
// 将字符串转换成字节流,因为底层的传输都是字节传输
byte[] data = msg.getBytes();
try {
// 对方ip和端口
DatagramPacket pack = new DatagramPacket(data, data.length, InetAddress.getByName(ip), 60011);
mSocket.send(pack);
Log.d(TAG, "发送成功!");
} catch (IOException e) {
Log.d(TAG, "发送失败!");
e.printStackTrace();
}
}
}).start();
}
/**
* 接收消息的函数
*/
public void receiveMessage(TextView tv) throws Exception {
// 创建线程,同理,接收信息也要放在线程里面接收
new Thread(new Runnable() {
public void run() {
try {
if (mSocket == null) {
mSocket = new DatagramSocket(60011);
}
String str;
while (true) {
// 创建一个空的字节数组
byte[] data = new byte[1024];
// 将字节数组和数组的长度传进DatagramPacket 创建的对象里面
DatagramPacket pack2 = new DatagramPacket(data, data.length);
Log.v("s", "pack2");
Log.v("s", "开始 接收");
try {
// socket对象接收pack包,程序启动的时候,socket会一直处于阻塞状态,直到有信息传输进来
mSocket.receive(pack2);
} catch (IOException e) {
e.printStackTrace();
}
// 获取发送数据的IP地址
String ip = pack2.getAddress().getHostAddress();
// 获取发送数据端的端口号
int port = pack2.getPort();
str = new String(pack2.getData(), 0, pack2.getLength()); // 将字节数组转化成字符串表现出来
// 开启接收端口后会持续接收数据,只有页面可见的时候才将收到的数据写入
tv.setText(str);
Toast.makeText(context, "IP地址:"+ip+"端口:"+port, Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
/**
* 关闭通信
*/
public void closeSocket() {
if (mSocket != null) {
mSocket.close();
mSocket = null;
}
}
}
activity_main:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="对方IP地址:"/>
<EditText
android:id="@+id/target_ip_ed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"/>
</LinearLayout>
<Button
android:id="@+id/decide_btn"
android:layout_width="80dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:gravity="center"
android:textSize="20sp"
android:text="确定"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="发送信息端:"/>
<EditText
android:id="@+id/send_message"
android:layout_width="match_parent"
android:layout_height="55dp"
android:textSize="20sp"
android:hint="发送的消息"
android:background="@color/teal_200"
android:gravity="center"/>
<Button
android:id="@+id/send_btn"
android:layout_width="80dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:gravity="center"
android:textSize="20sp"
android:text="发送"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="接收信息端:"/>
<TextView
android:id="@+id/receive_message"
android:layout_width="match_parent"
android:layout_height="55dp"
android:hint="接收到的消息"
android:textSize="20sp"
android:background="@color/purple_500"
android:gravity="center"/>
</LinearLayout>
</LinearLayout>
权限:
<uses-permission android:name="android.permission.INTERNET"/>