04_应用层

1. Socket

客户端和服务器端在通信前,双方都要建立一个套接字,然后再将套接字连接起来形成管道,进行数据传输。

在创建 Socket 时,需要指定是 IPv4 还是 IPv6,分别对应设置为 AF_INET 和 AF_INET6;是 TCP 还是 UDP,分别对应设置为 SOCK_STREAMSOCK_DGRAM。创建完成后,会返回一个描述符。

  • 描述符:用来在一台计算机内部识别套接字的机制;
  • 端口号:用来让通信的另一方能识别出套接字的机制;

1.1 基于 TCP 协议

1.1.1 服务器端
  • 首先服务端调用 socket() 函数创建一个 socket 描述符,参数中指定协议族、sokcet 类型、协议,唯一标识一个 socket,后续操作都有用到它;
  • 然后调用 bind 函数,参数中指定 socket 描述符、地址、socket 长度,将相应的地址和端口赋给 socket,也就是和 socket 相绑定。
  • 然后调用 listen 函数将 socket 变为被动的连接监听的 socket,参数中指定 socket 描述符、该 socket 可以排队的最大连接数。因为 socket 函数返回的 socket 默认都是主动连接的 socket,所以在服务端需将它转为被动等待连接的 socket;
  • 而 accept 函数位于 listen 函数后,默认会阻塞进程,直到有一个客户端请求连接;
  • 实现类是ServerSocket。
package CommunicationDemo;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

//模拟服务端
public class TcpServerDemo2 {
    public static void main(String[] args) throws Exception {
        //1.创建服务
        ServerSocket serverSocket = new ServerSocket(9000);
        //2.监听客户端连接 //阻塞式
        Socket socket = serverSocket.accept();
        //3.获取输入流
        InputStream is = socket.getInputStream();
        //4.文件输出
        FileOutputStream file = new FileOutputStream(new File(""));
        byte[] bytes = new byte[1024];
        int len;
        while((len=is.read(bytes))!=-1){
            file.write(bytes,0,len);
        }

        //通知客户端接收完毕
        OutputStream os = socket.getOutputStream();
        os.write("接收完毕".getBytes());

        //关闭资源
        file.close();
        is.close();
        socket.close();
        serverSocket.close();
    }

}
1.1.2 客户端
  • 指定远程服务器的ip地址和端口创建socket实例

    • 调用 connect 函数主动连接服务器,通过三次握手建立连接。连接过程不是由 connect 函数完成,它仅仅是通知 linux 内核,让 linux 内核自动完成 TCP 三次握手连接;通常 connect 默认会一直阻塞,直到三次握手成功或失败;
    • 服务端 accept 接收到请求后,就建立连接,然后会返回一个新的 socket原来的 socket 则继续监听其他客户端的连接请求。
  • 通过socket实例获取输出流和输出流进行数据的读写

  • 数据传输完毕,调用socket的close方法关闭连接

package CommunicationDemo;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

//模拟客户端下载文件
public class TcpClientDemo2 {
    public static void main(String[] args) throws Exception {
        //1.建立连接
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
        //2.创建输出流
        OutputStream os = socket.getOutputStream();
        //3.读取文件变成流
        FileInputStream file = new FileInputStream(new File(""));
        //4.写出文件流
        byte[] buffer = new byte[1024];
        int len;
        while((len=file.read(buffer))!=-1){
            os.write(buffer,0,len);
        }
        //通知服务器结束了
        socket.shutdownOutput();

        //确定服务端接受完毕
        InputStream is = socket.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer2 = new byte[1024];
        int len2;
        while((len2=is.read(buffer2))!=-1){
            baos.write(buffer2,0,len2);
        }
        System.out.println(baos.toString());


        //断开连接
        baos.close();
        is.close();
        file.close();
        os.close();
        socket.close();
    }

}

1.2 基于 UDP 协议

由于 UDP 没有连接,不需要三次握手,也就不需要 listenconnect 函数,但是 UDP 仍然需要 IP 地址和端口,因而需要 bind 函数。

UDP 没有维护连接状态,因而只需要一个 Socket,就能和多个客户端进行通信。

1.2.1 服务端
  • 指定本地端口创建DatagramSocket实例
  • 通过字节数组创建DatagramSocket实例,调用其receive方法来接受数据
  • 设置实例返回的数据、调用send方法发送
  • 传输完成关闭连接
package CommunicationDemo.UDPDemo;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
//接收端
public class UdpServerDemo01 {
    public static void main(String[] args) throws Exception {
        //开放端口
        DatagramSocket socket = new DatagramSocket(9090);
        //接收数据包
        byte[] bytes = new byte[1024];
        DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
        socket.receive(packet);
        System.out.println(packet.getAddress().getHostAddress());
        System.out.println(new String(packet.getData(),0, packet.getLength()));
        socket.close();
    }
}
1.2.2 客户端
  • 创建实例
  • 通过ip和端口发送数据包
  • 通过字节数组创建实例receive方法接受数据包
  • 传输完成关闭连接
package CommunicationDemo.UDPDemo;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
//发送端
public class UdpClientDemo01 {
    public static void main(String[] args) throws Exception {
        //1.建立socket
        DatagramSocket socket = new DatagramSocket();
        //2.建立包
        String msg = "你好,服务器";
        DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length,InetAddress.getByName("localhost"),9090);
        //3.发送包
        socket.send(packet);
        //4.关闭流
        socket.close();
    }
}

2. DNS 协议

用户与互联网上的主机通信时,需要将域名解析为 IP 地址,它需要由分布在互联网上的许多 DNS 服务器共同完成。

客户端在查询 IP 地址时,查询消息包含三种信息:

  • 域名:Web 服务器、邮件服务器的名称;
  • Class:用来识别网络,但一般 Class 的值都是代表互联网的 IN
  • 记录类型:表示域名对应何种类型的记录。例如,类型 A 表示域名对应的是 IP 地址;类型 MX 表示域名对应的是邮件服务器;类型 CNAME 表示域名对应的是相关别名等。

DNS 服务器中的信息都按照域名分层次进行保存,域名中越靠后的位置表示其层级越高。将管理下一级域的 DNS 服务器的 IP 地址注册到它们的上级服务器中,以此类推。这样就可以通过上级 DNS 服务器查询出下级 DNS 服务器的 IP 地址。所以,对于任意一个域的 DNS 服务器,都可以从根域开始依次找到。

域名服务器按照作用可划分四个类型:

  • 根域名服务器:最高层级的域名服务器,管理着 comcnnet 等顶级域名服务器的信息。根域名服务器信息会保存在互联网中所有的 DNS 服务器中。
  • 顶级域名服务器:管理在该顶级域名服务器注册的所有二级域名。
  • 权限域名服务器:它是负责一个区的域名服务器。
  • 本地域名服务器:当主机发出 DNS 查询请求时,查询请求首先会发送给本地域名服务器。

当应用进程需要把域名解析为 IP 地址时,该应用进程就调用解析程序,并成为 DNS 的一个客户,把待解析的域名放在 DNS 请求报文中,以用户数据报方式发送给本地域名服务器。本地域名服务器在查找到域名后,把对应的 IP 地址放在响应报文中。应用进程获取目的主机的 IP 地址即可进行通信。

域名解析的查询步骤如下:

  • 主机先向本地域名服务器发出 DNS 请求,进行递归查询;
  • 如果能找到对应的 IP 地址,就直接返回;否则就采用递归查询,先向根域名服务器进行迭代查询;
  • 根域名服务器告诉本地域名服务器,下一步应该查询的顶级域名服务器的 IP 地址;
  • 本地域名服务器向顶级域名服务器进行查询;
  • 顶级域名服务器告诉本地域名服务器,下一步应该查询的权威域名服务器的 IP 地址;
  • 本地域名服务器向权威域名服务器进行查询;
  • 权威域名服务器告诉本地域名服务器,所查询的主机的 IP 地址;
  • 本地域名服务器把查询结果返回给主机。

此外,有时并不需要从根域名服务器开始查询,因为 DNS 服务器中设置缓存保存了之前查询过的域名。如果要查询的域名已经在缓存中,就可以直接得到所需信息,接下来从缓存的位置开始向下进行。

2.1 使用的协议

在 DNS 系统中,有两种类型的 DNS 服务器,分别为主 DNS 服务器和辅助 DNS 服务器。

  • 在一个区中主 DNS 服务器从自己本机的数据文件中读取该区的 DNS 数据信息;
  • 而辅助 DNS 服务器则从区的主 DNS 服务器中读取该区的 DNS 数据信息。当一个辅助 DNS 服务器启动时,它需要与主 DNS 服务器通信,并加载数据信息,这就叫做区传送。

区域传送

区域传送时使用 TCP 协议,主要有以下两点考虑:

  1. 辅域名服务器会定时(一般是 3 小时)向主域名服务器进行查询,看数据是否有变动。如有变动,则会执行一次区域传送,进行数据同步。区域传送将使用 TCP,这是因为数据同步传送的数据量比较大,远超过 512 字节。
  2. 另外由于 TCP 是一种提供可靠服务的连接,保证了数据的准确性。

域名解析

域名解析时使用== UDP== 协议:

  • 客户端向 DNS 服务器查询域名时,一般返回的内容都不超过 512 字节,用 UDP 传输即可。不用经过 TCP 三次握手,这样 DNS 服务器负载更低,响应更快。

3. FTP 协议

FTP 是使用最广泛的文件传送协议,使用 TCP 可靠的运输服务,减少或消除不同操作系统下处理文件的不兼容性。

它使用客户服务器方式。一个服务器进程可同时为多个客户进程服务。可由两个部分组成:

  • 主进程,负责接受新的请求;
  • 若干个从属进程,负责处理单个请求。

主进程的工作步骤:

  • 服务器打开21 号端口,等待客户进程发出连接请求;
  • 收到连接请求后,启动从属进程进行处理。从属进程对客户进程的请求处理完毕后就会终止,但从属进程在运行期间根据需要还可能创建其他一些子进程;
  • 处理后主进程又回到等待状态,继续接受其他客户进程发来的请求。

如上图,FTP 的客户端和服务器端之间要建立两个并行的 TCP 连接:

  • 控制连接:控制连接会话期间一直保持打开,FTP 客户发出的传送请求,通过控制连接发送给服务器端的控制进程;
  • 数据连接:实际用来传输文件的。

当客户进程向服务器进程发出建立连接请求时,要寻找连接服务器进程的 21 号端口,同时还要告诉服务器进程自己的另一个端口号码,用于建立数据传送连接。接着,服务器进程用自己传送数据的 20 号端口与客户进程提供的端口号建立数据传达连接。

4. SMTP/POP3

SMTP 是简单的邮件传送协议,用于发送邮件。通常使用的是 25 端口。

POP3 与 SMTP 对应,用于接收邮件。通常使用的是 110 端口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值