(网络编程01))
目录应用层一、传输层的两个核心协议二、有链接/无连接是抽象的概念是虚拟的/逻辑上的连接例如要进行网络通信物理上的连接网线啥的三、可靠传输 vs 不可靠传输四、面向字节流 vs 面向数据报五、全双公 vs 半双工六、回升服务器重点应用层操作系统提供的一组 API socket API (传输层给应用层提供)socket 这个词的意思是插槽的意思对应在主板上可以理解为主板上的一些接口。一、传输层的两个核心协议TCP协议 和 UDP协议这两个差别非常大。TCP 是有连接可靠传输面向字节流全双工UDP 是无连接不可靠传输面向数据报全双工二、有链接/无连接是抽象的概念是虚拟的/逻辑上的连接例如要进行网络通信物理上的连接网线啥的为什么TCP 是可靠的传输有链接而 UDP 是不可靠传输无连接。对于 TCP 来说TCP 协议中就会保存对端的信息例如 A和B通信A和B先建立连接让A保存B的信息B保存A的信息彼此之间知道谁是和它连接的哪个。对于UDP来说UDP协议本身不保存对方的信息。就是无连接 。三、可靠传输 vs 不可靠传输网络上数据是非常容易出现丢失的情况丢包因为光信号/电信号都可能受到外界的干扰。本来是传输0101但是因为数据的丢包其中有些bit位就被修改了这样乱了的数据就会被识别出来把这样的数据极可能传输不到原来的地方。而且网络世界是通过路由器/交换机来进行联络的路由器/交换机类似于“十字路口”它也会发生我们生活中十字路口堵车的情况在某个时间点实际需要转发的数据超过了设备能转发的上线就会容易发生数据丢包问题。可靠传输的意思是不能保证数据包100%的到达而是尽可能的提高传输成功的概率。如果出现了丢包能够感知到。不可靠传输只是把数据发出去他到底是到了还是没到他就不管了。四、面向字节流 vs 面向数据报面向字节流读写数据的时候是以字节为单位的面向数据报读写数据的时候是以一个数据报为单位不是字符为单位一次必须读写一个 UDP 数据报不能是半个。五、全双公 vs 半双工全双工一个通信链路支持 双向通信 能读也能写半双工一个通信链路只支持单向通信只能读或者只能写他们俩就像我们的一条公路公路上的黄色线就是车道的分割线。全双工就像是双向公路这条路可以从A到B也可以从B到A。半双工就像是单向公路只能从A到B或者是从B到A。接下来就可以使用socket api 进行网络编程了在计算机中“文件”通常是一个“广义的概念”文件还能代指一些硬件设备操作系统管理硬件设备也是抽象成文件统一管理的。就像网卡抽象成socket文件操作网卡的时候流程和操作普通文件差不多 打开 - 读写 - 关闭。操作网卡直接不好操作把操作网卡当成操作socket文件就容易操作了。socket文件就相当于“网卡的遥控器”。六、回升服务器重点下面我们写一个回声服务器。回声服务器是一个客户端输入什么服务器端什么任务都不做直接返回客户端输入的内容先说一下大致流程创建一个接口这个接口的端口必须是固定的为的就是防止这个服务器今天在这个端口明天在另外一个端口端口非常随机我们很难去访问这个流动的服务器所以我们把端口给固定之后端口就不会跑了。这是类的创建和构造函数。Datagram是数据报,Socket是接口所以DatagramSocket是数据报的接口创建一个数据报的接口。public class UdpEchoServer01 { private DatagramSocket socket null; public UdpEchoServer01(int port) throws SocketException { // 指定一个固定端口号给服务器使用 socket new DatagramSocket(port); } }创建好接口之后因为这个接口是给服务器服务的所以我们需要不断的接收客户端的请求。public void start() throws IOException { System.out.println(服务器启动); while(true){ } }在不断请求中我们需要去获取客户端的请求所以我们需要创建一个数据报来接收请求的内容public void start() throws IOException { System.out.println(服务器启动); while(true){ DatagramPacket requestPacket new DatagramPacket(new byte[4096], 4096); } }上面我们创建好这个数据报之后去调用接口来给这个数据报读取客户端发来的请求。就能知道客户端要做什么了如果客户端没有请求的发送这里也会发生阻塞等待。我们需要一个byte数据来保存请求的数据所以创建这个请求数据报的时候需要传入一个byte 数组来保存请求。public void start() throws IOException { System.out.println(服务器启动); while(true){ DatagramPacket requestPacket new DatagramPacket(new byte[4096], 4096); //////////// 使用接口给 requestPacket 这个数据报中传入客户端的请求 socket.receive(requestPacket); } }经过requestPacket 这个请求数据报接收客户端发送的请求之后这个数据报内就保存好了客户端发送的数据报的内容我们把其中的客户端发送的请求给解析出来放到一个String中。因为是构造String所以我们获取到data之后需要从0开始读取然后构造这个数据报的长度的大小。public void start() throws IOException { System.out.println(服务器启动); while(true){ DatagramPacket requestPacket new DatagramPacket(new byte[4096], 4096); socket.receive(requestPacket); //////////////////////// 获取 requestPacket 中的 data String request new String(requestPacket.getData(), 0, requestPacket.getLength()); ////////////// 这里无法使用如果传入 getData().length 每次构造的长度都会是4096,会造成请求混乱 // String request new String(requestPacket.getData(), 0, requestPacket.getData().length); } }我们有了这个请求之后构造一个处理函数处理一下这个请求模拟一下真实服务器的运行。因为这里我们写的是回声服务器你也可以直接使用这个请求字符串来构造响应数据报。这里封装成一个函数来处理这个请求。public void start() throws IOException { System.out.println(服务器启动); while(true){ DatagramPacket requestPacket new DatagramPacket(new byte[4096], 4096); socket.receive(requestPacket); String request new String(requestPacket.getData(), 0, requestPacket.getLength()); //// 使用 response 接收响应 String response process(request); } } ///// 服务器处理请求返回响应的函数 public String process(String request){ return request; }拿到响应之后我们就可以构造数据报返回给客户端的接口这时候我们需要知道客户端的IP和端口这两个内容都在requestPacket接收客户端的请求的时候就保存起来了这时候我们可以构造一个响应数据报直接调用requestPacket里面的getAddress() 获取 IPgetPort() 获取端口。或者使用 getSocketAdderss() 直接获取客户端的 IP和端口。public void start() throws IOException { System.out.println(服务器启动); while(true){ DatagramPacket requestPacket new DatagramPacket(new byte[4096], 4096); socket.receive(requestPacket); String request new String(requestPacket.getData(), 0, requestPacket.getLength()); String response process(request); ///////////////// 直接使用 getSocketAdderss() DatagramPacket responsePacket new DatagramPacket(response.getBytes(),response.getBytes().length, requestPacket.getSocketAddress()); /////////////// 使用 getAddress() 和 gerPort() 分别获取 IP 和 端口号 // DatagramPacket responsePacket new DatagramPacket(response.getBytes(),response.getBytes().length, // requestPacket.getAddress(), requestPacket.getPort()); } } public String process(String request){ return request; }最后可以把响应数据报返回给客户端之后就是客户端来接受这个响应了。还可以打印一下服务器端日志requestPacket里面保存了客户端的IP和端口号。[IP:端口号] req: 请求 resp: 响应哪个 IP 的 机器哪个端口的程序 访问了服务器请求是什么服务器给的响应是什么public void start() throws IOException { System.out.println(服务器启动); while(true){ DatagramPacket requestPacket new DatagramPacket(new byte[4096], 4096); socket.receive(requestPacket); String request new String(requestPacket.getData(), 0, requestPacket.getLength()); String response process(request); DatagramPacket responsePacket new DatagramPacket(response.getBytes(),response.getBytes().length, requestPacket.getSocketAddress()); ////////// 把响应数据报返回给客户端 socket.send(responsePacket); ///////////// 打印一下服务器日志 System.out.printf([%s:%d] req: %s resp: %s\n, requestPacket.getAddress(), requestPacket.getPort(), request, response); } } public String process(String request){ return request; }