一, 概述
在学习 Java 基础的时候, 有一章节就叫《网络编程》, 那么何为网络编程呢? 在此之前先了解一下何为计算机网络.
计算机网络:
简单地说就是将地理位置不同的设备通过通信线路连接起来, 实现不同设备间的信息传递和资源共享的计算机系统.
网络编程:
实现不同计算机上程序之间进行数据交换的过程就叫网络编程. 比如我们常用的 QQ, 微信, 就是在不同设备之间进行数据交换, 就属于网络编程.
二, 网络通信三要素
互联网上的设备要进行通信, 有三个要素.
IP:
是设备在网络中的唯一标识, 也就是说, 一个 IP 就有唯一的一台设备, 根据 IP, 就可以找到唯一的一台设备.
找到了设备, 知道了要跟谁通信了, 那么要跟这台设备上的哪个程序进程通信呢? 那么就需要根据端口来判断.
端口:
用来区分设备上运行的不同的进程. 有效端口为 0 ~ 65535 , 其中 0 ~ 1024 是系统保留的端口.
根据 IP + 端口, 我们可以找到需要进行通信的设备和该设备上对应的进程, 按道理此时就可以进行通信了, 实际上不是这样的. 比如你是说中文的, 你找到的那台设备的那个进程只能识别英文, 那么语言就不互通了. 这时候就需要用到协议.
协议:
可以简单的理解协议就是通信规则. 让不同设备进行通信时可以语言互通. 常用的协议有 TCP 协议和 UDP 协议.
TCP | UDP |
---|---|
建立数据通道,可进行大量数据传输,效率较低,但安全可靠。 | 无需建立连接,使用数据包传递数据,每个数据包大小限制在 64k,效率高,不可靠。 |
三, Java 对网络编程的支持
Java 对网络编程提供了良好的支持, 其相关的类都在 java.NET 包下.
InetAddress 类:
这个类可以理解为就是表示 IP.
套接字:
套接字 = IP + 端口 . 通信的两端(客户端和服务端) 都需要有套接字, 套接字之间利用 IO 进行数据传输. 客户端和服务端使用的套接字不同, TCP 和 UDP 使用的套接字也不同. 如下表.
方式 | 客户端 | 服务端 |
---|---|---|
TCP | Socket | ServerSocket |
UDP | DatagramSocket、DatagramPacket | DatagramSocket、DatagramPacket |
四, Java 网络编程小案例
1, 使用 UDP 协议进行通信:
客户端 (发送端) 编程步骤:
创建发送端的 socket 对象;
创建数据, 并把数据打包;
调用 socket 的发送方法发送数据;
释放资源.
- public static void main(String[] args) throws Exception {
- //1. 创建发送端的 socket 对象
- DatagramSocket socket = new DatagramSocket();
- //2. 封装键盘录入的数据
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- String line = null;
- while ((line = br.readLine()) != null){
- if ("886".equals(line))// 如果输入 886 就停止服务
- break;
- //3. 创建数据, 并把数据打包
- byte[] bys = line.getBytes();// 创建数据
- int length = bys.length;
- InetAddress address = InetAddress.getByName("xxx.xxx.x.xxx");//IP
- int port = 10086;// 端口
- DatagramPacket dp = new DatagramPacket(bys,length,address,port);// 打包数据
- //4. 调用 socket 的发送方法发送数据
- socket.send(dp);
- }
- //5. 释放资源
- socket.close();
- }
服务端 (接收端) 编程步骤:
创建接收端的 socket 对象;
创建数据包, 用来接收数据;
调用 socket 对象的接收方法接收数据;
解析数据.
- public static void main(String[] args) throws Exception {
- //1. 创建接收端的 socket 对象
- DatagramSocket ds = new DatagramSocket(10086);
- while (true){
- //2. 创建一个数据包(接收容器)
- byte[] bys = new byte[1024];
- DatagramPacket dp = new DatagramPacket(bys,bys.length);
- //3. 调用 socket 对象的接收方法接收数据
- ds.receive(dp);// 阻塞式方法
- //4. 解析数据包, 显示在控制台
- String s = new String(dp.getData(),0,dp.getLength());
- System.out.println(dp.getAddress().getHostAddress()+":"+s);
- }
- }
其实这样就是一个简易的聊天室了, 先开启服务端, 然后开启客户端, 在控制台用键盘录入数据, 这些数据就会在服务端的控制台显示出来. 我们可以用多线程改进一下, 以达到启动一个 main 方法, 又可以发送又可以接收的效果. 代码如下:
- public class SendThread implements Runnable{
- private DatagramSocket ds;
- public SendThread(DatagramSocket ds) {
- this.ds = ds;
- }
- @Override
- public void run() {
- try {
- //2. 封装键盘录入的数据
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- String line = null;
- while ((line = br.readLine()) != null){
- if ("886".equals(line))
- break;
- //3. 创建数据, 并把数据打包
- DatagramPacket dp = new DatagramPacket(
- line.getBytes(),
- line.getBytes().length,
- InetAddress.getByName("xxx.xxx.x.xxx"),
- 12306);
- //4. 调用 socket 的发送方法发送数据
- ds.send(dp);
- }
- //5. 释放资源
- ds.close();
- }catch (IOException e){
- e.printStackTrace();
- }
- }
- public class ReceiveThread implements Runnable {
- private DatagramSocket ds;
- public ReceiveThread(DatagramSocket ds) {
- this.ds = ds;
- }
- @Override
- public void run() {
- try {
- while (true){
- //2. 创建一个数据包(接收容器)
- byte[] bys = new byte[1024];
- DatagramPacket dp = new DatagramPacket(bys,bys.length);
- //3. 调用 socket 对象的接收方法接收数据
- ds.receive(dp);// 阻塞式方法
- //4. 解析数据包, 显示在控制台
- String s = new String(dp.getData(),0,dp.getLength());
- System.out.println(dp.getAddress().getHostAddress()+":"+s);
- }
- }catch (IOException e){
- e.printStackTrace();
- }
- }
- }
- public class chatRoom {
- public static void main(String[] args) throws Exception {
- DatagramSocket send = new DatagramSocket();
- DatagramSocket receive = new DatagramSocket(12306);
- SendThread st = new SendThread(send);
- ReceiveThread rt = new ReceiveThread(receive);
- Thread t1 = new Thread(st);
- Thread t2 = new Thread(rt);
- t1.start();
- t2.start();
- }
- }
以上就是通过多线程改进的简易的聊天室.
2, 使用 TCP 协议进行通信:
客户端编程步骤:
创建发送端的 socket 对象;
获取输出流写数据;
释放资源.
- public static void main(String[] args) throws Exception {
- //1. 创建发送端的 socket 对象
- Socket socket = new Socket("xxx.xxx.x.xxx",2222);
- //2. 获取输出流写数据
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
- String line = null;
- while ((line = br.readLine()) != null){
- if ("886".equals(line))
- break;
- bw.write(line);
- bw.newLine();
- bw.flush();
- }
- //3. 释放资源
- socket.close();
- }
服务端编程步骤:
创建接收端的 socket 对象;
监听客户端连接;
获取输入流读取数据;
释放资源.
- public static void main(String[] args) throws IOException {
- //1. 创建接收端的 socket 对象
- ServerSocket serverSocket = new ServerSocket(2222);
- //2. 监听客户端连接
- Socket socket = serverSocket.accept();
- //3. 获取输入流读取数据, 输出到文本文件
- BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
- BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
- String line = null;
- while ((line = br.readLine()) != null){
- bw.write(line);
- bw.newLine();
- bw.flush();
- }
- //4. 释放资源
- bw.close();
- socket.close();
- }
总结:
网络编程三要素就是 IP, 端口, 协议.
协议主要是 UDP 和 TCP 两种. 不管使用何种协议, 客户端和服务端都需要有 socket 对象.
UDP 协议是将数据打包, 每个数据包大小限制为 64k, 不建立连接, 传输数据不可靠, 但效率高.
TCP 协议会建立连接通道, 传输的数据大小无限制, 安全可靠, 但效率较低.
来源: http://www.jianshu.com/p/3a8df885a294