我是靠谱客的博主 现实棉花糖,这篇文章主要介绍【Java成王之路】EE初阶第八篇:(网络编程) 2,现在分享给大家,希望可以做个参考。

上节回顾

网络初识:

1.网络的基本的一些概念

2.协议

3.协议分层

4.封装和分用(是网络传输数据的具体流程,封装就是在数据中添加一些辅助传输的信息,分用就是就是解析这些信息)

发送数据的时候,上层协议要把数据交给下层协议,由下层协议来添加一些信息

接收数据的时候,下层协议要把数据交给上层协议,由上层协议来进行进一步的解析

socket  

网络编程,通过代码,来控制,让两个主机的进程之间能够进行数据交互.

好比:我使用qq发送一个消息,这个消息就是通过我电脑上的qq客户端进程,先发送给了腾讯服的务器(对应的服务器进程),再由腾讯的服务器进程,把这个消息转发给对方电脑的qq进程.

操作系统就是把网络编程的一些相关操作(相关操作指的是:1.操作系统提供的功能2;访问网络核心的硬件设备,网卡,网卡也是归操作系统来管理的),封装起来了,提供了一组AP供程序猿来调用

操作系统提供的这组API叫做:socket(也可以称为套接字) API

socket的英文原意:叫做插槽 

由于操作系统提供的socket api是C语言风格的接口,咱们在Java中是不能直接使用的.但是没关系,JDK其实也针对C语言这里的socket api  进行了封装.

在标准库中有一组类,这组类就能够让我们完成网络编程.

这组类本质上仍然是调用的操作系统提供的socket API 

Java能调用C语言的函数嘛?

答案是:可以的!

这种调用叫做"跨语言调用"

不同的语言之间,很多都可以互相调用.

要想能够跨语言调用,核心原理在于,了解对应的语言的ABI(二进制编程接口) 

操作系统,提供的API主要有两类(实际上不止这两类,还有其他的)

1.流套接字(底层使用了TCP协议)

2.数据报套接字(底层使用了UDP协议)

简单介绍TCP、UDP

都是传输层协议

socket API 也是属于传输层的东西.

 我们是在应用程序中使用socket API.

socket API也都是传输层协议暴露给上面的应用层的(TCP,UDP)

TCP:

1.有连接

2.可靠传输

3.面向字节流

4.全双工

UDP:

1.无连接

2.不可靠传输

3.面向数据报

4.全双工

有连接:好比就是打电话(连接建立好了才能通电话,如果对面把电话挂了,连接关闭了就没法通电话了)

无连接:好比就是发微信(,回车键一敲,消息就发过去,不管对面看的到还是看不到,想接收还是不想接收,总之消息都能发出去)

可靠传输:发送方能够知道对方是不是收到了

所谓的可靠传输,不是说发送的数据100%就能被对方收到!!(这件事是不可能的),技术再牛逼,你也抵不过拔网线.

不可靠传输:发送方不知道对方是不是收到了.

打电话是否是可靠传输?

是!

发微信是否是可靠传输?

不是!

可靠性 != 安全性

面向字节流:之前介绍过,假设为了发送100个字节,可以一次发送一个字节,重复100次,也可以发送10个字节,重复10次....可以非常灵活的完成这里的发送,接收也是同理.

面向数据报:以一个一个的数据报为基本单位(每个数据报多大,不同的协议里面是有不同的约定的)发送的时候一次至少发一个数据报(如果尝试发一个半,实际只能发出去一个).接收的时候,一次至少接收一个数据报.(如果尝试接收半个,剩下半个就没了)

全双工:双向通信.A和B可以同时向对方发送接收数据

半双工:单向通信.要么A给B发,要么B给A发,不能同时发.

UDP socket 中有两个核心的类:

1.DatagramSocket 描述一个socket 对象.

2.DatagramPacket 描述一个UDP数据报

操作系统提供的网络编程API叫做socket API

socket API中涉及到一个核心概念socket.

socket本质上是一个文件描述符(某个进程被创建出来,进程就会对应一个PCB.PCB中就包含了一个文件描述符表.每次打开一个文件,就会在文件描述符表中分配一个表项.文件描述符表类似于一个数组.数组的下标就是文件描述符,数组的元素是一个内核结构struct file(C语言中的结构体.Linux内核本身就是C语言写的))

一切皆文件:

操作系统在管理硬件设备和一些软件资源的时候.

为了能够风格统一,于是就像用文件的方式来管理

普通文件是文件

键盘 => 标准输入文件

显示器 => 标准输出文件

 网卡也是一个硬件设备.操作系统也是用文件来管理网卡.

此处用来表示网卡设备的文件,就是socket文件.

要想操作网卡,就需要先创建出socket文件.

通过读写这个socket文件的方式来操作网卡了

这个Java标准库中的DatagramSocket对象其实就是在表示一个socket文件.

面向数据报(.DatagramPacket),发送/接收数据,就是以.DatagramPacket对象为单位进行的.

receive()方法:接收数据,如果没有数据过来,receive就会阻塞等待,如果有数据了,receive就能返回一个.DatagramPacket对象.

send()方法:发送数据,以.DatagramPacket为单位进行发送

发送的时候,需要知道发送的目标在哪

接收的时候,也需要知道这个数据从哪来?

ip地址+端口号

在Java里面用InetSocketAddress类表示.

相当于把(ip地址+端口号)这里信息一打包就构成了一个InetSocketAddress类,就可以用这个类表示要发送或者接收具体的一个位置.

UDP的 socket 简单编写一个回显程序.回显客户端 + 回显服务器

 回显:A给B说啥,B就回应啥.(复读机)

回显程序本身没啥意义,但是当前通过这个程序主要是为了能够熟悉Socket API具体的使用

正常的客户端/服务器的通信流程:

服务器代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; /** * Created with IntelliJ IDEA. * Description: * User: 灯泡和大白 * Date: 2022-07-25 * Time: 13:52 */ public class UdpEchoServer { private DatagramSocket socket = null; // port 表示端口号. // 服务器在启动的时候, 需要关联(绑定)上一个端口号. // 收到数据的时候, 就会根据这个端口号来决定把数据交给哪个进程. // 虽然此处 port 写的类型是 int, 但是实际上端口号是一个两个字节的无符号整数. // 范围 0-65535 public UdpEchoServer(int port) throws SocketException { socket = new DatagramSocket(port); } // 通过这个方法来启动服务器. public void start() throws IOException { System.out.println("服务器启动!"); // 服务器一般都是持续运行的(7*24) while (true) { // 1. 读取请求. 当前服务器不知道客户端啥时候发来请求. receive 方法也会阻塞. // 如果真的有请求过来了, 此时 receive 就会返回. // 参数 DatagramPacket 是一个输出型参数. socket 中读到的数据会设置到这个参数的对象中. // DatagramPacket 在构造的时候, 需要指定一个缓冲区(就是一段内存空间, 通常使用 byte[]). DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096); socket.receive(requestPacket); // 把 requestPacket 对象里面的内容取出来, 作为一个字符串. String request = new String(requestPacket.getData(), 0, requestPacket.getLength()); // 2. 根据请求来计算响应. String response = process(request); // 3. 把响应写回到客户端. 这时候也需要构造一个 DatagramPacket // 此处给 DatagramPacket 中设置的长度, 必须是 "字节的个数". // 如果直接取 response.length() 此处得到的是, 字符串的长度, 也就是 "字符的个数" // 当前的 responsePacket 在构造的时候, 还需要指定这个包要发给谁. // 其实发送给的目标, 就是发请求的那一方. DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, requestPacket.getSocketAddress()); socket.send(responsePacket); // 4. 加上日志打印. // %d 表示要打印一个有符号十进制的整数. %s 表示要打印一个字符串. // 如果使用 + 字符串拼接是否可行? 完全可以! 只不过写起来会比较麻烦. // 不建议使用字符串拼接. String log = String.format("[%s:%d] req: %s; resp: %s", requestPacket.getAddress().toString(), requestPacket.getPort(), request, response, "hello"); // String log = "[" + requestPacket.getAddress().toString() + ":" + requestPacket.getPort() + "] " ..... System.out.println(log); } } // 此处的 process 方法负责的功能, 就是根据请求来计算响应. // 由于当前是一个 回显服务器 , 就把客户端发的请求直接返回回去即可. private String process(String request) { return request; } public static void main(String[] args) throws IOException { UdpEchoServer server = new UdpEchoServer(9090); server.start(); } }

 客户端代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.io.IOException; import java.net.*; import java.util.Scanner; public class UdpEchoClient { private DatagramSocket socket = null; private String serverIp; private int serverPort; //客户端一启动的时候,就需要知道服务器的 ip 和端口 //但是服务器一启动的时候,是无法知道客户端的 ip 和 端口的.直到客户端的请求到了,服务器才知道对应客户端的ip 和 端口. public UdpEchoClient(String sereverIp,int serverPort) throws SocketException { this.serverIp = sereverIp; this.serverPort = serverPort; this.socket = new DatagramSocket(); } public void start() throws IOException { Scanner scanner = new Scanner(System.in); while (true) { //1.从标准输入读入一个数据 System.out.print("->"); String request = scanner.nextLine(); if(request.equals("exit")){ System.out.println("exit"); return; } //2.把字符串构造成一个 UDP 请求,并发送数据 //这个DatagramPacket中,既要包含具体的数据,又要包含这个数据发给谁 DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(serverIp), serverPort); socket.send(requestPacket); //3.尝试从服务器这里读取响应 DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096); socket.receive(responsePacket); String response = new String(responsePacket.getData(),0,responsePacket.getLength()); //4.显示这个结果 String log = String.format("req: %s; resp: %s",request,response); System.out.println(log); } } public static void main(String[] args) throws IOException { UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090); client.start(); } }

 

 

 

打印结果:

 

 

 

最后

以上就是现实棉花糖最近收集整理的关于【Java成王之路】EE初阶第八篇:(网络编程) 2的全部内容,更多相关【Java成王之路】EE初阶第八篇:(网络编程)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(117)

评论列表共有 0 条评论

立即
投稿
返回
顶部