TCP/IP基础笔记(一)
网上找了一张图示你就能懂了,我也是按照这张图进行学习的。
![](https://img2018.cnblogs.com/blog/1411524/201903/1411524-20190301093622653-487838780.png)
UDP
简单概念:
UDP 是User Datagram Protocol的简称, 翻译过来就是 用户数据报协议 。是 OSI(Open System Interconnection,开放式系统互联) 参考模型中 一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务 。
解释一下:ISO是一个组织,叫什么不重要,这个组织制定了OSI模型 ,OSI模型把网络通信的工作分成了七层:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。UDP参考了传输层的协议。不过该协议(理解为协议族)已经被TCP/IP 4层模型淘汰的协议,现在学习更倾向于后者 。
UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。
UDP在IP报文中的位置图示:
UDP 报文结构图示:
UDP 报文由两部分组成:报头和具体数据
报头:由4个域组成,其中每个域各占用2个字节
源端口号:为不同的应用保留其各自的数据传输通道,在需要对方回信时选用,不需要时可全 0;
目的端口号:数据接收一方通过目标端口接收数据;
UDP长度:指包括报头和数据部分在内的总字节数,报头的长度是固定的,最小值为 8;
UDP检验和:保证数据的安全,由发送方产生发送到接收方进行校验,检测 UDP 用户数据报在传输中是否有错,有错就丢弃;
UDP 主要特性:
(1) UDP是一个无连接协议,传输数据之前源端和终端不建立连接,减少数据发送前的延时,仅仅受应用程序生成数据的速度、计算机的能力和传输带宽的限制;
(2) 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。
(3) UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。
(4) 吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。
(5) UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态表(这里面有许多参数),所以UDP是不可靠协议。
(6) UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小。
应用场景:
代码实现UDP客户端服务端通信的demo:https://www.cnblogs.com/yongdaimi/p/5983558.html
DatagramSocket 和 DatagramPacket 两个类是 基于UDP协议进行通信的包装类 一 DatagramSocket DatagramSocket():创建实例。通常用于客户端编程,它并没有特定监听的端口,仅仅使用一个临时的。 DatagramSocket(int port):创建实例,并固定监听Port端口的报文。 DatagramSocket(int port, InetAddress localAddr):这是个非常有用的构建器,当一台机器拥有多于一个IP地址的时候,由它创建的实例仅仅接收来自LocalAddr的报文 InetAddress IP 地址实例 此类没有构造器,通过静态方法调用获取实例 二 DatagramPacket 用于处理报文,将byte数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成byte数组。 DatagramPacket(byte[] buf, int length, InetAddress addr, int port):从buf数组中,取出length长的数据创建数据包对象,目标是addr地址,port端口。 DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):从buf数组中,取出offset开始的、length长的数据创建数据包对象,目标是addr地址,port端口。 DatagramPacket(byte[] buf, int offset, int length):将数据包中从offset开始、length长的数据装进buf数组。 DatagramPacket(byte[] buf, int length):将数据包中length长的数据装进buf数组。 getData():它从实例中取得报文的byte数组编码。
UDP服务端代码:
public class UtpServer { public static void main(String[] args)throws IOException { String str_send = "Hello UDPclient"; byte[] buf = new byte[1024]; //服务端在3000端口监听接收到的数据 DatagramSocket ds = new DatagramSocket(3000); //接收从客户端发送过来的数据 DatagramPacket udp_receive = new DatagramPacket(buf, 1024); System.out.println("server is on,waiting for client to send data......"); boolean f = true; while(f){ //服务器端接收来自客户端的数据 ds.receive(udp_receive); System.out.println("server received data from client:"); String str_receive = new String(udp_receive.getData(),0,udp_receive.getLength()) + " from " + udp_receive.getAddress().getHostAddress() + ":" + udp_receive.getPort(); System.out.println(str_receive); //数据发动到客户端的3000端口 DatagramPacket dp_send= new DatagramPacket(str_send.getBytes(),str_send.length(),udp_receive.getAddress(),9000); ds.send(dp_send); //由于dp_receive在接收了数据之后,其内部消息长度值会变为实际接收的消息的字节数, //所以这里要将dp_receive的内部消息长度重新置为1024 udp_receive.setLength(1024); } ds.close(); } }
UDP客户端代码:
public class UtpClient { private static final int TIMEOUT = 5000; //设置接收数据的超时时间 private static final int MAXNUM = 5; //设置重发数据的最多次数 public static void main(String args[])throws IOException{ //发送的数据 String sendMes = "Hello UDPServer,I am UDPClient!!!"; byte[] buf = new byte[1024]; //客户端在9000端口监听接收到的数据 DatagramSocket ds = new DatagramSocket(9000); //获取 InetAddress 实例 InetAddress loc = InetAddress.getLocalHost(); //定义用来发送数据的DatagramPacket实例 DatagramPacket udp_send= new DatagramPacket(sendMes.getBytes(),sendMes.length(),loc,3000); //定义用来接收数据的DatagramPacket实例 DatagramPacket udp_receive = new DatagramPacket(buf, 1024); //数据发向本地3000端口 ds.setSoTimeout(TIMEOUT); //设置接收数据时阻塞的最长时间 int tries = 0; //重发数据的次数 boolean receivedResponse = false; //是否接收到数据的标志位 //直到接收到数据,或者重发次数达到预定值,则退出循环 while(!receivedResponse && tries<MAXNUM){ //发送数据 ds.send(udp_send); try{ //接收从服务端发送回来的数据 ds.receive(udp_receive); //如果接收到的数据不是来自目标地址,则抛出异常 if(!udp_receive.getAddress().equals(loc)){ throw new IOException("Received packet from an umknown source"); } //如果接收到数据。则将receivedResponse标志位改为true,从而退出循环 receivedResponse = true; }catch(InterruptedIOException e){ //如果接收数据时阻塞超时,重发并减少一次重发的次数 tries += 1; System.out.println("Time out," + (MAXNUM - tries) + " more tries..." ); } } if(receivedResponse){ //如果收到数据,则打印出来 System.out.println("client received data from server:"); String str_receive = new String(udp_receive.getData(),0,udp_receive.getLength()) + " from " + udp_receive.getAddress().getHostAddress() + ":" + udp_receive.getPort(); System.out.println(str_receive); //由于dp_receive在接收了数据之后,其内部消息长度值会变为实际接收的消息的字节数, //所以这里要将dp_receive的内部消息长度重新置为1024 udp_receive.setLength(1024); }else{ //如果重发MAXNUM次数据后,仍未获得服务器发送回来的数据,则打印如下信息 System.out.println("No response -- give up."); } ds.close(); } }