网络编程
1. 软件结构
现在互联网主流的软件结构分为两大类:
- B/S结构: 全称是Browser/Server结构,是指浏览器/服务器结构。常见的的浏览器有谷歌、搜狐等。
- C/S结构: 全称是Client/Server结构,是指客户端/服务器结构。常见的有QQ、迅雷等软件。
2. 网络通信协议
- 网络通信协议: 通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中这些连接和通信的规则被称为网络通信协议,它对数据的格式传输、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。
- TCP/IP协议: 传输控制协议/因特网互联协议(Transmission Control Protocol/Internet Protocol),是Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型(物理层、网络层、传输层、应用层),每一层都呼叫它的下一层所提供的协议来完成自己的需求。
3. 协议的分类
通信协议比较复杂,
java.net
包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,专注于网络程序开发,而不用考虑通信的细节。
- UDP: 用户数据报协议(User Datagram Protocol)。UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时,不建议使用UDP协议。 - 特点: 数据被限制在64kb以内,超出这个范围就不能发送了。
数据报(Datagram): 网络传输的基本单位 - TCP: 传输控制协议(Transmission Control Protocol)。TCP协议时面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过"三次握手"。
- 三次握手: TPC协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠性。
- 第一次握手,客户端向服务器端发出连接请求,等待服务器确认
- 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接。
4. 网络编程三要素
协议
计算机网络通信必须遵守的规则。上面已经详细谈及过。
IP地址
指的是互联网协议地址(Internet Protocol Address),俗称IP。IP地址用于给一个网络中的计算机做唯一的编号。IP地址就好比电话号码。
IP地址的分类
- IPv4: 由32位二进制数组成,通常会分为4个字节,表示为
a,b,c,d
的形式,例如:192.168.32.55。其中a,b,c,d
必须是0~255之间的十进制整数,这是因为每一个字节都是8位二进制数,最大的表示数应该是2的8次方-1。 - IPv6: 由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,表示成
ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。
常用命令
- 查看本机IP地址
ipconfg
- 检查网络是否联通
ping 空格 ip地址
特殊的IP地址
本机地址: localhost
、127.0.0.1
端口号
网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,那么在网络通信时,就需要端口号来区分进程。
IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的进程(应用程序)。
取值范围
端口号由两个字节组成,取值范围在0-65535之间(两个字节等于16位二进制数,所以最大值是2的16次方-1)
- 1024之前的端口号我们不能使用,因为已经被系统分配给已知的网络软件。
- 网络软件的端口号不能重复。
常用的端口号
- 网络端口 80(所有网址默认)
- 数据库端口号 mysql:3306 oracle:1521
- Tomcat服务器 8080
TCP通信程序
1. 概述
TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端与服务器端。
两端通信时的步骤:
- 服务器端程序需要先启动,等待客户端的连接
- 客户端主动连接服务器端,连接成功才能通信。服务器端不可以主动连接客户端
在Java中,提供了两个类用于实现TCP通信程序:
- 客户端:
java.net.Socket
。创建Socket对象,向服务器端发出连接请求,服务器端响应请求,两者建立连接开始通信。 - 服务器端:
java.net.ServerSocket
。创建ServerSocket类对象,相当于开启一个服务,并等待客户端的连接。
注意事项
- 客户端和服务器端建立的逻辑连接包含一个IO对象,由于通信的数据不仅仅是字符,所以IO对象是字节流。
- 客户端和服务器端进行一个数据交互,需要四个IO流对象。
- 实际上服务器端并不会存在IO流,服务器端通过accept()方法可以获取客户端的Socket对象。使用客户端的IO流去和客户端进行交互。
2. Socket类
此类实现客户端套接字。套接字是两台机器间通信的端点。
套接字: 包含了IP地址和端口号的网络单位
构造方法:
- Socket(String host,int post) 创建一个流套接字并将其连接到指定主机上的指定端口
- host 服务器主机的名称
- post 服务器的端口号
成员方法:
- InputStream getInputStream() 返回此套接字输入流
- OutputStream getOutputStream() 返回此套接字的输出流
- void close() 关闭此套接字。
使用步骤:
- 创建一个客户端Socket对象,构造方法绑定服务器的IP地址和端口号
- 使用Socket对象中的getOutputStream()方法获取网络字节输出流OutputStream对象
- 使用网络字节输出流OutputStream对象中的write方法给服务器发送数据
- 使用Socket对象中的getInputStream()方法获取网络字节输入流InputStream对象
- 使用网络字节输入流InputStream对象中的read方法读取服务器回复的数据
- 释放资源
注意事项:
- 客户端和服务器端进行交互,必须使用Sokcet中提供的网络流(SocketInputStream和SokcetOutputStream),不可以使用自己创建的流对象
- 当我们创建客户端Sokcet对象的时候,就回去请求服务器并且经过三次握手建立连接通路(如果服务器没有启动,就会抛出异常ConnectException)
3. ServerSocket类
此类实现了服务器的套接字
构造方法:
- ServerSocket(int port) 创建绑定到特定端口的服务器套接字
成员方法:
- Socket accept() 侦听并接收到此套接字的连接
使用步骤:
- 创建服务器ServerSocket对象和系统要指定的端口号
- 使用ServerSocket对象中的accept方法,获取到请求的客户端Socket对象
- 使用Socket对象中的getInputStream()方法获取网络字节输入流InputStream对象
- 使用网络字节输入流InputStream对象中的read方法读客户端发来的数据
- 使用Socket对象中的getOutputStream()方法获取网络字节输出流OutputStream对象
- 使用网络字节输出流OutputStream对象中的write方法给客户端回复数据
- 释放资源(如果需要持续使用的话,不需要关闭ServerSocket对象)
服务器端开启后不会自动关闭,会一直等待状态(直至客户端发送信息)。
4. 使用案例
这个例子我希望做一个类似聊天软件一样的程序,实现服务器端和客户端的在线聊天。(由于设备有限,以同一台机子在线通信为例。)
首先,我们大致规划一下要做的步骤。
- 创建一个服务器端,这个服务器端可以保持监听状态,可以发送和接收消息。
- 创建一个客户端,这个客户端可以连续连接服务器并发送和恢复消息。
服务器端代码实现
public class TCPServer {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象,指定端口。
ServerSocket server = new ServerSocket(8888);
//通过while循环让服务器保持监听状态(死循环)
while(true){
//获取客户端socket对象
Socket socket = server.accept();
//获取客户端的消息
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
while((len = is.read(bytes)) != -1){
System.out.print("接收到的消息为: ");
System.out.println(new String(bytes,0,len));
System.out.println();
}
//回复消息
OutputStream os = socket.getOutputStream();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入回复的消息");
os.write(scanner.nextLine().getBytes());
//由于客户端发过来的内容没有结束标志,导致服务器端的输出流的read方法一直在阻塞,
//因此需要用shutdownOutput方法来解决阻塞。
socket.shutdownOutput();
socket.close();
}
}
}
值得一提的是shutdownOutput()方法。由于网络输出流不会发送结束标记,因此会导致接收一方的read方法陷入阻塞,从而程序不再往下执行。使用这个方法可以让输出流结束并且自动附上结束标记。
客户端代码实现
public class TCPClient {
public static void main(String[] args) throws IOException {
while(true){
//创建Socket对象,指定主机和接口
Socket socket = new Socket("localhost",8888);
//发送消息给服务器端
OutputStream os = socket.getOutputStream();
System.out.println("请输入需要发送的消息");
Scanner scanner = new Scanner(System.in);
os.write(scanner.nextLine().getBytes());
socket.shutdownOutput();
//接收服务器端的信息
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
while ((len = is.read(bytes)) != -1) {
System.out.print("接收到的消息为: ");
System.out.println(new String(bytes, 0, len));
System.out.println();
}
}
}
}
运行结果
实现不同计算机的在线聊天,只需要修改IP地址并且有访问权限就好了!