Java 网络编程基础学习

学习过程是看毕向东老师的视频。


1.网络模型

OSI参考模型
应用层
表示层
会话层
传输层
网络层
数据链路层
物理层

面试的时候,技术总监最后和我说到这里时,我表示一脸蒙蔽,说不出这7层名字都是啥。


TCP/IP 参考模型
应用层
传输层
网络层
主机至网络层

根据我目前的了解,我是在最上层应用层开发。

网络通讯要素:

  • IP地址
  • 端口号
  • 传输协议

2 TCP与UDP

  • TCP(Transmission Control Protocol 传输控制协议)

在OSI中属于第四层传输层的协议。是一种面向连接的,可靠的,基于字节流的的传输层协议。在因特网协议族(Internet protocol suite)中,TCP层是位于IP层之上,应用层之下的中间层。

应用层向TCP层发送用于网间传输的、用8位字节表示的数据流,然后TCP把数据流分区成适当长度的报文段(通常受该计算机连接的网络的数据链路层的最大传输单元(MTU)的限制)。之后TCP把结果包传给IP层,由它来通过网络将包传送给接收端实体的TCP层。TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。

Java 网络编程基础学习_第1张图片
TCP三次握手

TCP三次握手的过程如下:

  1. 客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。
  2. 服务器端收到SYN报文,回应一个SYN(SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。
  3. 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。

  • UDP(User Datagram Protocol用户数据报协议)

同TCP协议同层,UDP也是第四层传输层的协议。是无连接的,不可靠的,协议。

UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但是即使是在今天UDP仍然不失为一项非常实用和可行的网络传输层协议。
与所熟知的TCP(传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层。根据OSI(开放系统互连)参考模型,UDP和TCP都属于传输层协议。UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。

以上主要摘抄百度百科。


3.TCP和UDP的特点

TCP特点

  • 建立连接,形成传输数据的通道
  • 在连接中进行大量的数据传输
  • 三次握手,可靠
  • 由于必须建立连接,效率稍低

UDP特点

  • 将数据、源和目的封装成数据包,不需要建立连接
  • 每个数据报的大小限制在64k
  • 不可靠,可能丢包
  • 由于不需要建立连接,所以速度快

常见的使用场景
UDP:聊天,视频会议
TCP:下载文件


4.Socket

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket。两个Socket间通信主通过IO。Socket进行通信所需要的协议不只有TCP/IP,在Java中主要就是TCP/IP协议。

TCP/IP协议

Transmission ControlProtocol/InternetProtocol的简写,中译名为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。
通俗而言:TCP负责发现传输的问题,一有问题就发出信号,要求重新传输,直到所有数据安全正确地传输到目的地。而IP是给因特网的每一台联网设备规定一个地址。


TCP通信需要SocketServerSocket两个类。
客户端对应Socket,服务端对应ServerSocket
TCP建立两个Socket的过程:
当客户端有Socket试图连接服务端某个目标端口时,监听这个服务端目标端口的ServerSocket就会接受(accept)这个客户端的Socket请求,ServerSocket并在服务端建立一个Socket来与客户端进行通信。这时,客户端和服务端便各有了一个Socket。


UDP客户端和服务端的两个Socket进行通信,需要DatagramSocketDatagramPacket这两个封装好的类。

DatagramSocket

This class represents a socket for sending and receiving datagram packets.

DatagramPacket

This class represents a datagram packet.

感觉英文反而容易理解一些。
DatagramPacket:把数据进行封装的数据报包。作用是用来实现无连接的包投递服务。
DatagramSocket:负责发送和接受数据报包。


5.UDP简单实例

客户端向服务端发送Hello World!

客户端代码:

public class UdpSend {

    public static void main(String[] args) {
        String info = "Hello World";
        int port = 10888;
        boolean b = sendMessage(info, port);
        if (b) {
            System.out.println("ok!!!");
        }
    }

    private static boolean sendMessage(String info, int port) {
        try {
            // 1 创建DatagragmSocket对象
            int me = 10887;
            DatagramSocket ds = new DatagramSocket(me);
            // 2 创建DatagramPacket对象
            byte[] buf = info.getBytes();// byte[]
            InetAddress address = InetAddress.getByName("192.168.0.103");// IP
            DatagramPacket dp = new DatagramPacket(buf, buf.length, address, port);
            // 3 发送数据
            ds.send(dp);
            // 4 关闭连接
            ds.close();

            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

在客户端代码中,客户端端口号为10887,服务端端口号为10888.
DatagramSocket构造方法中的端口号为自己的端口号10887。
DatagramPacket构造方法中也需要一个端口号,但这个端口号为服务端,接收DatagramPacket这个数据报包的DatagramSocket的端口号10888。


服务端代码:

public class UdpReceiver {
    public static void main(String[] args) {
        int port = 10888;
        receString(port);
    }

    private static void receString(int port) {
        try {
            // 1 创建DatagramSocket对象 确定端口
            DatagramSocket ds = new DatagramSocket(port);
            // 2 创建DatagramPacket
            byte[] buf = new byte[1024 * 8];
            DatagramPacket dp = new DatagramPacket(buf, buf.length);
            // 3 将接收到的数据放入DatagramPacket中
            ds.receive(dp);
            // 4 利用DatagramPacket的方法拿到具体的数据
            String ip = dp.getAddress().getHostAddress();
            int p = dp.getPort();
19行:       String info = new String(dp.getData(), 0, dp.getLength());
            System.out.println(ip + "\n" + info + "\n" + p);
            // 关闭连接
            ds.close();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

需要注意的是19行中,拿String数据时,通过dp.getLength()这个方法,根据DatagramPacket中数据的时机大小来确定。因为定义的buf的大小为1024 * 8

还有就是记得关闭连接。


6.TCP简单实例

客户端代码:

public class TcpClient {
    public static void main(String[] args) {
        String info = "Hello world!";
        boolean b = sendMessage(info);
        if (b) {
            System.out.println("ok");
        }
    }

    private static boolean sendMessage(String info) {
        int port = 10902;
        try {
            InetAddress address = InetAddress.getByName("192.168.0.103");
            // 1 建立Socket
            Socket socket = new Socket(address, port);
            // 2 拿到输出流
            OutputStream outputStream = socket.getOutputStream();
            // 3 写入数据
            outputStream.write(info.getBytes());
            // 4 关闭Socket
            socket.close();
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

服务端代码:

public class TcpServer {
    public static void main(String[] args) {
        receInfo();
    }

    private static void receInfo() {
        try {
            // 1 建立ServerSocket
            ServerSocket serverSocket = new ServerSocket(10902);
            // 2 通过accept()拿到Socket
            Socket socket = serverSocket.accept();
            // 打印IP
            System.out.println(socket.getInetAddress().getHostAddress());
            // 3 通过Socket拿到输入流
            InputStream inputStream = socket.getInputStream();
            byte[] buf = new byte[1024];
            int len;
            // 4 读取数据
            while ((len = inputStream.read(buf)) != -1) {
                String info = new String(buf, 0, len);
                System.out.println(info);
            }
            // 5 关闭Socket和ServerSocket
            socket.close();
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端Socket和服务端ServerSocket的端口号为一个。ServerSocket就是通过端口号和客户端的Socket通过accept()建立联系。


7.最后

UDP通信依赖DatagramSocketDatagramPacket
TCP通信依赖SocketServerSocket

Java Socket网络编程最基础的部分有了一点了解,还有很多东西就需要以后继续深入学习。有错误希望可以指出。: )

共勉。

你可能感兴趣的:(Java 网络编程基础学习)