TCP/IP基础笔记(一)

 

TCP/IP
TCP/IP 狭义的理解,是指TCP 和 IP 两种协议,实际生活当中有时也确实就是指这两种协议。
其实不够严谨,它是利用 IP 进行通信时所必须用到的协议群的统称。
具体来说,IP 或 ICMP、TCP 或 UDP、TELNET 或 FTP、以及 HTTP 等都属于 TCP/IP 协议。TCP/IP 一词泛指这些协议,因此,有时也称 TCP/IP 为网际协议群。
网上找了一张图示你就能懂了,我也是按照这张图进行学习的。
 

 

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客户端服务端通信的demohttps://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();
    }
}

 

posted @ 2019-02-28 15:26  最咸的那一条  阅读(154)  评论(0编辑  收藏  举报