网络编程可以让程序与网络上的其他设备中的程序进行数据交互。
常见的网络通信基本模式有:Client-Server(CS,客户端-服务端)、Browser-Server(BS,浏览器-服务端)。
概述:IP(Internet Protocol):全称是”互联网协议地址“,是分配给上网设备的唯一标志。
常见的 IP 分类:IPv4(如点分十进制表示:192.168.1.66)和 IPv6(可以用冒分十六进制表示)。
IP 地址形式:公网地址、私有地址(局域网)。192.168.
开头的就是常见的局域网地址,范围为 192.168.0.0
到 192.168.255.255
,专门为组织机构内部使用。
IP 常用命令:ipconfig(查看本机 IP)、ping IP地址或者域名(检查网络是否联通)。
特殊 IP 地址:本机 IP 为:127.0.0.1
或 localhost
,这两个称为回送地址或本地回环地址,只会寻找当前所在本机。
InetAddress 类表示 Interent 协议(IP)地址。
常用 API | 说明 |
---|---|
public static InetAddress getLocalHost() | 返回本主机的 IP 地址对象,static 方法 |
public static InetAddress getByName(String host) | 得到指定主机的 IP 地址对象,static 方法 |
public String getHostName() | 获取此 IP 对象的主机名 |
public String getHostAddress() | 获取此 IP 对象的地址字符串 |
public boolean isReachable(int timeout) | 在指定毫秒内连通该 IP 地址对应的主机,连通则返回 true,相当于 ping 操作 |
注意:getByName 方法中参数可以是域名或者是 IP 地址,返回结果为对应的 IP 地址对象。
端口号:唯一标识正在计算机设备上运行的进程(程序),被规定为一个 16 位的二进制,范围为 0-65535。
端口类型分为:周知端口、注册端口以及动态端口。
注意:我们自己开发的程序选择注册端口,且一个设备中不能出现两个程序的端口号一样,否则出现端口冲突错误。相反,不同的设备中相同的程序端口号应该相同。
网络通信协议:计算机网络中,连接和通信数据的规则。
网络通信协议有两套参考模型:OSI 参考模型、TCP/IP 参考模型(国际标准)。
TCP/IP 参考模型分为:数据链路层、物理层、网络层、传输层(TCP、UDP 协议)、应用层(程序员一般在这一层开发)。
传输层的两个常见协议:TCP、UDP。
TCP 协议:Transmission Control Protocol,传输控制协议。
协议特点:
通信场景:对信息安全要求较高的场景,例如文件下载、金融等数据通信等。
UDP 协议:User Datagram Protocol,用户数据报协议。
协议特点:
通信场景:语音通话、视频会话等。
UDP 有三种通信方式:单播(一发一收、多发一收)、广播(一发多收)、组播(一发多收)。
使用 UDP 通信实现一对一客户端发送消息、服务器端接收消息。
总体思路:
发送端 or 接收端 | 构造器 or 方法名 | 说明 |
---|---|---|
发送端 | public DatagramSocket() | 创建发送端对象 |
同上 | public DatagramPacket(byte buf[], int length, InetAddress address, int port) | 创建发送端数据包对象 |
同上 | public void send(DatagramPacket p) | 发送数据包 |
同上 | public void close() | 关闭资源 |
接收端 | public DatagramSocket(int port) | 创建接收端对象 |
同上 | public DatagramPacket(byte buf[], int length) | 创建接收端数据包对象 |
同上 | public void receive(DatagramPacket p) | 接收数据包 |
同上 | public int getLength() | 得到接收到的实际数据大小 |
同上 | public void close() | 关闭资源 |
注意:
创建发送端的 Socket 对象时,系统会随机分配一个端口号,也可以指定端口号。
创建接收端的 Socket 对象时,需要在构造器中指定端口号。
创建发送端数据包对象使用的构造器 DatagramPacket 中参数分别为:要发送内容的字节数组、要发送内容的字节长度、接收端的 IP 地址对象、接收端的端口号。
创建接收端数据包对象使用的构造器 DatagramPacket 中参数分别为:字节数组用来存储接收的内容,能够接收内容的长度。
在接收端如果想知道接收到的实际数据大小,则要调用数据包对象的 getLength 方法。
运行时,应该先启动接收端,再启动发送端。
接收端拓展的 API,都是数据包对象调用
方法名 | 说明 |
---|---|
public SocketAddress getSocketAddress() | 获得发送端 IP 地址对象和端口号 |
public InetAddress getAddress() | 获得发送端 IP 地址对象 |
public int getPort() | 获得发送端的端口号 |
在一发一收的基础上,在发送端和接收端分别加 while 循环,如果输入 exit 则退出。
需求:三个客户端给一个服务器端发消息,实现多发多收。
运行结果
注意:UDP 的接收端可以接收很多发送端的消息,接收端只负责接收数据包,而无所谓是哪个发送端的数据包。
一发多收分为广播和组播。
广播是指当前主机与所在网络中的所有主机通信,步骤:
255.255.255.255
,并且指定端口;注意:广播的数据包不会指定到某个 IP 地址对象,而是用广播地址,这样就发送给了所在网段的其他主机。
组播是指当前主机与选定的一组主机的通信,步骤:
224.0.0.0
到 239.255.255.255
。注意:用
DatagramSocket
的子类MulticastSocket
创建的对象可以在接收端调用joinGroup
方法绑定组播 IP。
在 java 中只要是使用了 java.net.Socket 类实现通信的,底层就是使用了 TCP 协议。
客户端发送消息步骤:
socket
对象,请求与服务端的连接;socket
对象调用 getOutputStream
方法得到字节输出流;服务端接收消息步骤:
ServerSocket
对象,注册服务端端口;ServerSocket
对象的 accept
方法,等待客户端的连接,并得到 socket
对象;socket
对象调用 getInputStream
方法得到字节输入流;客户端 or 服务端 | 构造器 or 方法名 | 说明 |
---|---|---|
客户端 | public Socket(String host, int port) | 创建客户端的 Socket 对象,请求与服务端的连接 |
同上 | public OutputStream getOutputStream() | 获得字节输入流对象 |
服务端 | public ServerSocket(int port) | 注册服务端端口 |
同上 | public Socket accept() | ServerSocket 对象的方法,等待客户端的连接 |
同上 | public InputStream getInputStream() | 得到字节输入流对象 |
同上 | public SocketAddress getRemoteSocketAddress() | Socket 对象的方法,得到客户端的 socket 地址 |
注意:
- 创建客户端的 Socket 对象时,参数一为服务端的 IP 地址,参数二为服务端的端口。
- 客户端怎么发,服务端就应该怎么收,如客户端发一行消息,服务端就应该收一行消息。
- 客户端如果没有消息,服务端就会进入阻塞等待。
- Socket 一方关闭或者出现异常,对方的 Socket 也会失效或者出错,所以最好不要关闭 Socket 管道。
拓展:需要一对一,发送多行消息,接收多行消息。
思路:在客户端加 while 循环,将服务端的 if 改为 while 循环。
上面的代码不能实现同时接收多个客户端的消息,因为服务端现在是单线程的,每次只能与一个客户端进行通信。而 UDP 的接收端可以接收很多发送端的消息,是因为接收端只负责接收数据包,而无所谓是哪个发送端的数据包,没有建立一对一通信的管道。
实现服务端可以接收多个客户端消息的方法:
代码实现
运行结果
注意:上面代码中提示上下线操作也可以在客户端和服务端实现。
使用上面的代码实现多发一收,客户端与服务端是 1:1 的关系,这样客户端并发越多,系统瘫痪就越快。
所以引入线程池来处理多个客户端消息,有以下优点:
代码实现
运行结果
即时通信是指一个客户端的消息发送出去,其他客户端可以接收到,之前 TCP 通信的消息都是发给服务端的,即时通信需要进行端口转发的思想,消息还是要先发给服务端,然后服务端再发给其他客户端。
实现思路:
三、3
的基础上,在客户端增加收消息的功能。注意:
- 因为客户端在发消息的同时,也随时有可能接收消息,所以客户端发送和接收消息应该用两个线程,用线程池来接收消息,用主线程来发送消息。
- 实际过程中,每个客户端发送消息用到一个线程就可以搞定,所以在客户端发送消息时,用不到线程池;而服务端,需要接收来自许多客户端的消息,所以与每个客户端都需要建立 Socket 管道,所以服务端的接收消息功能,就需要用到线程池。
- 在服务端端口转发时,不需要知道客户端的 IP 地址和端口号,因为在接收消息时,Socket 管道已经建立,直接传输数据就好。
实现
运行结果
上面的内容都是 CS 架构,客户端需要我们自己开发实现。
BS 架构是浏览器访问服务端,不需要开发客户端。
实现 BS 开发,服务器必须给浏览器相应 HTTP 协议格式的数据,否则浏览器不识别。
代码实现
在浏览器搜索栏输入服务器地址和端口号:127.0.0.1:6666
,运行结果
Win + R
。