网络编程
什么是计算机网络:
计算机网络是指多台计算机组成的网络。在一个网络中任何两台计算机都可以直接通信。计算机之间的通信也必须遵循一定的规则,我们叫做协议,比如 TCP、UDP、HTTP、Ftp、smtp等。就好比方言,两个人交流就必须使用同一种语言,广东人对北京人说粤语北京人就听不懂。计算机世界也一样,两个计算机之间通信也必须遵循同一个协议。
那怎么找到计算机呢?通过 IP 和端口号可以定位一台唯一的计算机。
IP:一个 IP 地址唯一标识一个网络接口。一台接入互联网的计算机至少有一个IP地址。IP地址又分为公网IP和内网Ip,内网IP大多是 192.168.x.x / 10.x.x.x 这样。
如果一个计算机只有一个网卡,那么,它又一个本机地址127.0.0.1,还有一个ip地址,例如 101.202.10.1,可以通过这个ip地址和互联网通信。如果两台计算机位于同一个网段就可以直接通信。就是他们前段是相同的,比如 192.168.10.1与192.168.100.9。如果不在同一个网段,两台计算机就不能直接通信,需要借助路由或者交换机间接通信,我们把这种设备称之为网关。网关的作用就是连接多个网络,把来自一个网络的数据包发送到另一个网络,这个过程叫做路由。
端口:端口范围为 0~65535。共有端口1~1023被公共服务使用,比如http:80、https:443、ftp:21、telent:23。普通程序可以使用的端口范围1024~49151,比如tomcat:8080、mysql:3306、oracle:1521等。动态、私有端口范围49152~65535。
当今的互联网之间的通信使用的是 TCP/IP 协议。模型如下:
IP 协议只负责发数据包,不能保证顺序和正确性。而 TCP 协议支持可靠传输和双向通信。TCP 协议建立在 IP 协议之上,负责控制数据包传输,它在数据包传输之前需要先建立连接,建立连接后才能传输数据,传输完成后还要断开连接。TCP 之所以能保证数据的可靠传输,是通过接受确认、超时重传机制实现的。并且支持双向通信。
TCP编程
同一台计算机同一时间会运行多个网络应用程序,例如浏览器、微信、qq、邮件客户端等,计算机接收到的数据包是怎么知道给哪个程序呢?所以操作系统就抽象出了socket接口,每个应用程序对应不同的socket,数据才能正确地发给对应的应用程序。
一个socket就是由IP地址和端口号组成,可以简单地把socket理解成IP地址加端口号。一个程序就是一个进程,使用socket进行网络编程时,就是两个进程之间的通信。其中一个进程会充当服务器端,会监听指定的某个端口,另一个进程充当客户端,连接服务器的IP地址和指定端口,如果连接成功,服务器端和客户端就建立了一个tcp连接,双方就可以通信了。
- 服务器示例
public class Server {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(6666);
InputStream inputStream = serverSocket.accept().getInputStream()) {
byte[] bytes = new byte[1024];
int len;
while ((len = inputStream.read(bytes)) != -1) {
String str = new String(bytes, 0, len);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
以上代码指定 6666 端口监听,没有指定 IP 地址,标识在计算机所有网络接口上进行监听。serverSocket.accept() 表示每当有新客户端连接进来时就创建一个新的 Socket 实例,这个 Socket 就是和建立连接的客户端通信的。如果没有客户端连接进来,accept() 就会阻塞并一直等待。我们这里没有考虑多个客户端的情况,如果有多个客户端同时请求,就必须为每一个 Socket 实例创建一个线程。
- 客户端示例
public class Client {
public static void main(String[] args) throws UnknownHostException {
// 获得服务器的地址和端口号
InetAddress serverIp = InetAddress.getByName("127.0.0.1");;
int port = 6666;
try (Socket socket = new Socket(serverIp, port);
OutputStream outputStream = socket.getOutputStream();) {
outputStream.write("TCP编程测试".getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Socket流:
当 Socket 连接创建成功后,无论服务器端还是客户端,我们都使用 Socket 实例进行网络通信。因为 TCP 是一种基于流的协议,因此,Java 标准库使用 InputStream 和 OutputStream 来封装 Socket 的数据流。
这里我们还用到了 try-with-resources 的写法来关闭流。
UDP编程
和 TCP 相比,UDP简单的多,因为 UDP 没有建立连接的过程,数据包时一次性收发一个,所以没有流的概念。但是 Java 中使用 UDP编程,仍然要使用 Socket,因为在使用 UDP 编程的时候,必须指定 IP 和端口号。但是要注意的是,UDP 和 TCP 虽然端口号的范围都是 0~65535,但他们是两套独立的端口,一个应用程序使用 TCP 占用了1234的端口,不影响另一个应用程序使用 UDP 站用1234的端口。