一、网络编程简介

生产环境下,通常网络编程和前面 线程结合起来使用过。
网络: 可以用来连接计算机和计算机之间的通讯,分为:局域网,广域网,城域网
局域网:例如公司内部,班级内部的这种内部网 :192.168.XXX.XXX
广域网:连接国家之间,国家内部的网络
城域网:连接城市内部的网络
网络编程:通过上述的网络技术,来实现计算机之间内部程序的 数据交互

1.1 网络编程-OSI模型

早期:由于计算机硬件 和 操作系统软件上 相互可能存在不匹配的情况,而导致计算机之间无法正常使用 网络进行通讯
后来:为了解决这个问题,国际标准化组织ISO 制定了 计算机之间通讯时,需要采用一种通讯模型 这个模型就叫OSI模型
OSI (操作系统通讯接口)

1.2 OSI七层通讯模型(了解)

无标题.png
自上而下:应用层,表现层,会话层,传输层,网络层,数据链路层,物理层

上三层

应用层,表现层,会话层
上三层都跟我们的程序有一定的关系。
应用层:就是我们的程序
表现层:对应用层的数据进行转换,变成计算机能认识的数据
会话层:建立计算机之间的会话通讯通道

传输层

将上三层的数据 和 下三层的数据, 完成数据的传输

下三层

网络层,数据链路层,物理层
下三层都跟操作系统的底层有关,或者是跟物理硬件有关
网络层:就是我们IP,Mask地址这些东西
数据链路层:在硬件之间进行数据以 ‘帧’为单位的 传输层
物理层:就是硬件 网卡 网线 路由器 交换机 ……

1.3 TCP/IP分层模型(记住)

上述的OSI 7层太过于复杂,所以就简化为4层模型
无标题.png
应用层:上三层,就是我们的应用程序
传输层:将上三层的数据 和 下三层的数据 进行双向传输
网络互连层:使用IP建立网络之间连接
网络接口层:计算机通讯网络入口

1.4 IP、端口、防火墙

无标题.png
IP地址和域名:都是家的家庭地址
端口:防火墙和家的门
防火墙:家外的院墙
不要轻易关闭防火墙

二、网络传输协议

2.1 网络通信的端

客户端:Client

胖客户端:需要你使用安装包进行安装客户端
瘦客户端:就是浏览器

服务端:Server

2.2 C/S架构 和 B/S架构的区别

1、不同的地方
C/S架构模式系统:客户端需要你安装的
B/S架构模式系统:客户端就是浏览器,不需要你安装
2、不同的地方
C/S架构模式系统:如果客户端程序,需要更新,那么就需要下载 更新包
B/S架构模式系统:如果客户端程序,需要更新,只需要刷新浏览器即可(例如:淘宝做改版,你只需要重新刷新淘宝页面即可)
我们以后工作,都是以B/S为主,C/S在我们程序就是一种辅助。

2.3 客户端和服务端通讯协议

从通讯协议上面来讲:底层协议就2大体系:TCP/IP协议,UDP通讯
HTTP协议,WEBSOCKET协议,这些都是针对TCP/IP协议的上层封装

2.4 Tcp/IP协议

Tcp/Ip协议,是一种端到端的,并且依赖连接的,而且通讯安全的协议。
因为采用的是:1问1答模式
例如:打电话
无标题.png

2.4.1 连接的建立方式

Tcp/Ip协议,通讯之前,计算机需要先建立 连接。但是它建立连接时,为了保证一定连接建立成功,所以它采用一种“三次握手”的建立规则
三次握手的具体步骤:
第一步:客户端 向 服务器 发送建立连接的请求 SYN(客户端的标识,代表客户端是谁)
第二步:服务端 向 客户端 回复可以建立连接,并颁发 ACK(客户端标识,这个标识是服务端颁发,用来服务端识别 多个客户端)
第三步:客户端 向 服务端 正式建立连接
三次握手完毕之后,就采用1问1答模式,进行数据交互
无标题.png
类似于谈恋爱:
男孩 想要约会一个女孩,首先会给女孩说:今晚,出来吃饭吧
女孩回复说:可以啊,在哪里呢?
男孩回复说:出来,我们一起去海底捞吃“火锅”吧

2.4.2 连接的断开方式

Tcp/IP协议,断开通讯之前,需要“四次挥手”的过程,原因是:它同样需要确保断开成功,以及清理数据成功
四次挥手的具体步骤
第一步:客户端请求 断开连接
第二步:服务端回复 收到请求
第三步:服务端回复 你的数据已清空,可以断开连接
第四步:客户端回复 OK,那我就断开(正式断开)
无标题.png
类似情侣分手:
第一步:男孩向女孩提出分手
第二步:女孩回复:你好绝情啊
第三步:女孩把你的所有东西,扔之门外
第四步:男孩说:那我们就永远不再见吧

2.4.3 连接分类

长连接

建立连接之后,长时间保持这个连接

短连接

建立连接之后,我们通讯一次之后,立马断开连接;下次如果需要再和你通讯,那么就需要建立新的连接

三、Socket通讯(面试的重灾区)

Socket 又被称为“套接字”。有一个层叫传输层,传输层的作用:将上三层的数据 和下三层的数据进行 转换
套接字:实际上就是咱们传输层用来传输数据的 一种载体 (通道)

3.1 Socket套接字编程

套接字编程中,核心类,都在java.net包下:
无标题.png
使用其中的哪些类取决于程序所需处理的通讯协议。例如,基于TCP的应用程序可使用 Socket、ServerSocket 等类;基于UDP的应用程序则使用DatagramPacket、DatagramSocket、MulticastSocket 等类;基于 HTTP 和 FTP 等协议直接访问 URL 资源的应用程序可使用 URL、URLConnection 等类。

3.2 TCP/IP协议-套接字编程

初级案例(1)-客户端向服务端发送消息

  1. package com.woniuxy.java26.study.socket.tcp;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.io.InputStreamReader;
  6. import java.net.ServerSocket;
  7. import java.net.Socket;
  8. /**
  9. * TCP/IP协议的服务端(代码)
  10. *
  11. * @author Administrator
  12. *
  13. */
  14. public class SocketServerStudy {
  15. public static void main(String[] args) {
  16. // TODO Auto-generated method stub
  17. System.out.println("主线程启动!!!!!");
  18. ServerSocket server = null;
  19. try {
  20. //创建Socket 的服务端,并指定8080 为通讯端口
  21. server = new ServerSocket(8080);
  22. //接收客户端(该方法是个阻塞式方法)
  23. Socket socket = server.accept();
  24. //输入流
  25. InputStream in = socket.getInputStream();
  26. //转换流
  27. InputStreamReader isr = new InputStreamReader(in,"UTF-8");
  28. //缓冲流
  29. BufferedReader br = new BufferedReader(isr);
  30. //边读边响应
  31. String str = "";
  32. while((str = br.readLine()) != null) {
  33. System.out.println("服务器接收的数据是:" + str);
  34. }
  35. } catch (IOException e) {
  36. // TODO Auto-generated catch block
  37. e.printStackTrace();
  38. }
  39. }
  40. }
  1. package com.woniuxy.java26.study.socket.tcp;
  2. import java.io.BufferedWriter;
  3. import java.io.IOException;
  4. import java.io.OutputStream;
  5. import java.io.OutputStreamWriter;
  6. import java.net.Socket;
  7. import java.net.UnknownHostException;
  8. public class SocketClientStudy {
  9. public static void main(String[] args) {
  10. // TODO Auto-generated method stub
  11. //本机使用127.0.0.1 或者 localhost
  12. Socket socket = null;
  13. try {
  14. socket = new Socket("127.0.0.1", 8080);
  15. //建立传输流
  16. OutputStream os = socket.getOutputStream();
  17. OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
  18. BufferedWriter bw = new BufferedWriter(osw);
  19. //向服务端发送数据
  20. bw.write("这是一个TCP/IP的测试信息!!!!");
  21. bw.flush();
  22. try {
  23. Thread.sleep(200);
  24. } catch (InterruptedException e) {
  25. // TODO Auto-generated catch block
  26. e.printStackTrace();
  27. }
  28. bw.close();
  29. osw.close();
  30. os.close();
  31. } catch (UnknownHostException e) {
  32. // TODO Auto-generated catch block
  33. e.printStackTrace();
  34. } catch (IOException e) {
  35. // TODO Auto-generated catch block
  36. e.printStackTrace();
  37. }
  38. }
  39. }

初级案例(2)-服务端支持多客户端

将socket.accept()方法,置于while(true){}

  1. package com.woniuxy.java26.study.socket.tcp;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.io.InputStreamReader;
  6. import java.net.ServerSocket;
  7. import java.net.Socket;
  8. /**
  9. * TCP/IP协议的服务端(代码)
  10. *
  11. * @author Administrator
  12. *
  13. */
  14. public class SocketServerStudy {
  15. public static void main(String[] args) {
  16. // TODO Auto-generated method stub
  17. System.out.println("主线程启动!!!!!");
  18. ServerSocket server = null;
  19. try {
  20. //创建Socket 的服务端,并指定8080 为通讯端口
  21. server = new ServerSocket(8080);
  22. while(true) {
  23. //接收客户端(该方法是个阻塞式方法)
  24. Socket socket = server.accept();
  25. //输入流
  26. InputStream in = socket.getInputStream();
  27. //转换流
  28. InputStreamReader isr = new InputStreamReader(in,"UTF-8");
  29. //缓冲流
  30. BufferedReader br = new BufferedReader(isr);
  31. //边读边响应
  32. String str = "";
  33. while((str = br.readLine()) != null) {
  34. System.out.println("服务器接收的数据是:" + str);
  35. }
  36. }
  37. } catch (IOException e) {
  38. // TODO Auto-generated catch block
  39. e.printStackTrace();
  40. }
  41. }
  42. }
    package com.woniuxy.java26.study.socket.tcp;
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    import java.net.UnknownHostException;
    public class SocketClientStudy {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            //本机使用127.0.0.1  或者 localhost
            Socket socket = null;
             try {
                 socket = new Socket("127.0.0.1", 8080);
                //建立传输流
                OutputStream os = socket.getOutputStream();
                OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
                BufferedWriter bw = new BufferedWriter(osw);
                //向服务端发送数据
                bw.write("这是一个TCP/IP的测试信息!!!!");
                bw.flush();
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                bw.close();
                osw.close();
                os.close();
            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

3.3 Socket编程的步骤(面试题)

1、使用ServerSocket 类,监听某一个端口
2、使用socket.accept(),等待接收客户端的连接
3、使用Socket类,使用IP地址,端口号,创建客户端与服务端的连接

初级案例(3)-服务端向客户端发送文件

    package com.woniuxy.java26.study.socket.tcp;
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    /**
     * TCP/IP协议的服务端(代码)
     * 
     * @author Administrator
     *
     */
    public class SocketServerStudy {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            System.out.println("主线程启动!!!!!");
            ServerSocket server = null;
            try {
                //创建Socket 的服务端,并指定8080 为通讯端口
                server = new ServerSocket(8080);
                while(true) {
                    //接收客户端(该方法是个阻塞式方法)
                    Socket socket = server.accept();
                    //建立流
                    OutputStream os = socket.getOutputStream();
                    OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
                    BufferedWriter bw = new BufferedWriter(osw);
                    //操作流
                    bw.write("服务器说:天王盖地虎!!!");
                    bw.newLine();
                    bw.write("服务器说:你脸红什么!!!");
                    bw.newLine();
                    bw.write("END");
                    bw.newLine();
                    bw.flush();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    package com.woniuxy.java26.study.socket.tcp;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.Socket;
    import java.net.UnknownHostException;
    public class SocketClientStudy {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            //本机使用127.0.0.1  或者 localhost
            Socket socket = null;
             try {
                 socket = new Socket("127.0.0.1", 8080);
                 InputStream in = socket.getInputStream();
                 InputStreamReader isr = new InputStreamReader(in, "UTF-8");
                 BufferedReader br = new BufferedReader(isr);
                 String str = "";
                 while(!(str = br.readLine()).equals("END")) {
                     System.out.println("客户端接收的消息是:" + str);
                 }
                 socket.close();
            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

初级案例(4)-使用线程完成服务端与客户端的读写文件分离

    package com.woniuxy.java26.study.socket.tcp;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.ArrayBlockingQueue;
    import com.woniuxy.java26.study.socket.handle.ServerReaderTask;
    import com.woniuxy.java26.study.socket.handle.ServerWriterTask;
    /**
     * TCP/IP协议的服务端(代码)
     * 
     * @author Administrator
     *
     */
    public class SocketServerStudy {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            System.out.println("主线程启动!!!!!");
            ServerSocket server = null;
            try {
                // 创建Socket 的服务端,并指定8080 为通讯端口
                server = new ServerSocket(8080);
                while (true) {
                    // 接收客户端(该方法是个阻塞式方法)
                    Socket socket = server.accept();
                    //定义一个传输队列(FIFO)
                    ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(1024);
                    // 读
                    Thread t1 = new Thread(new ServerReaderTask(socket,queue));
                    t1.start();
                    // 写
                    Thread t2 = new Thread(new ServerWriterTask(socket,queue));
                    t2.start();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    package com.woniuxy.java26.study.socket.handle;
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.Socket;
    import java.net.SocketException;
    import java.util.concurrent.ArrayBlockingQueue;
    /**
     * 服务端的 读取任务
     * 
     * @author Administrator
     *
     */
    public class ServerReaderTask implements Runnable {
        /**
         * 代表连接通道
         */
        private Socket socket;
        /**
         * 数据传输通道
         */
        private ArrayBlockingQueue<String> queue;
        public ServerReaderTask(Socket socket, ArrayBlockingQueue<String> queue) {
            super();
            this.socket = socket;
            this.queue = queue;
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            // 输入流
            InputStream in = null;
            InputStreamReader isr = null;
            BufferedReader br = null;
            try {
                if(!socket.isClosed() && socket.isConnected()) {
                    // 获得客户端的IP地址
                    String hostName = socket.getInetAddress().getHostName();
                    System.out.println(hostName);
                    in = socket.getInputStream();
                    // 转换流
                    isr = new InputStreamReader(in, "UTF-8");
                    // 缓冲流
                    br = new BufferedReader(isr);
                    // 读取消息
                    String str = "";
                    while (!(str = br.readLine()).equals("END")) {
                        System.out.println("服务器接收[" + hostName + "]的消息:" + str);
                    }
                    queue.add("[" + hostName + "] 的消息,我已收到!!!");
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                if(e instanceof SocketException) {
                    System.out.println("该连接已被重置!!!!");
                }
            }
        }
    }
    package com.woniuxy.java26.study.socket.handle;
    import java.io.BufferedWriter;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    import java.net.SocketException;
    import java.util.concurrent.ArrayBlockingQueue;
    /**
     * 服务端的 写入任务
     * 
     * @author Administrator
     *
     */
    public class ServerWriterTask implements Runnable {
        /**
         * 代表连接通道
         */
        private Socket socket;
        /**
         * 数据传输通道
         */
        private ArrayBlockingQueue<String> queue;
        public ServerWriterTask(Socket socket, ArrayBlockingQueue<String> queue) {
            super();
            this.socket = socket;
            this.queue = queue;
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            //建立传输流
            OutputStream os = null;
            OutputStreamWriter osw = null;
            BufferedWriter bw = null;
            try {
                if(!socket.isClosed() && socket.isConnected()) {
                    os = socket.getOutputStream();
                    osw = new OutputStreamWriter(os, "UTF-8");
                    bw = new BufferedWriter(osw);
                    while(!queue.isEmpty()) {
                        bw.write(queue.poll());
                        bw.newLine();
                    }
                    bw.flush();
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                if(e instanceof SocketException) {
                    System.out.println("该连接已被重置!!!!");
                }
            }
        }
    }
    package com.woniuxy.java26.study.socket.tcp;
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    public class SocketClientStudy {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            // 本机使用127.0.0.1 或者 localhost
            Socket socket = null;
            // 建立传输流
            OutputStream os = null;
            OutputStreamWriter osw = null;
            BufferedWriter bw = null;
            try {
                socket = new Socket("127.0.0.1", 8080);
                if (!socket.isClosed() && socket.isConnected()) {
                    os = socket.getOutputStream();
                    osw = new OutputStreamWriter(os, "UTF-8");
                    bw = new BufferedWriter(osw);
                    for(int i = 0; i < 10; i ++) {
                        System.out.println("客户端输出:" + i);
                        Thread.sleep(2000);
                        bw.write("长江,长江,我是黄河!!!!");
                        bw.newLine();
                    }
                    bw.write("END");
                    bw.newLine();
                }
                bw.flush();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                if (!socket.isClosed() && socket.isConnected()) {
                    try {
                        bw.close();
                        osw.close();
                        os.close();
                        socket.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
    }

四、UDP协议(了解)

UDP协议,是一种端到端,但是不依赖连接的,而且通讯不安全的协议。
因为采用的是:只管发,不管有没有接收到 例如:发短信,发微信,发QQ信息

4.1 UDP协议的用法

TCP/IP:ServerSocket Socket ,那么UDP就依赖的是DatagramSocket,DatagramPacket
UDP: 服务端和客户端 DatagramSocket,DatagramPacket

    package com.woniuxy.java27.study;
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.SocketException;
    public class UDPServerStudy {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            try {
                //数据缓冲区
                byte[] data = new byte[10240];
                //定义一个UDP服务端,监听的端口是9090
                DatagramSocket server = new DatagramSocket(9090);
                //定义用来接收数据的数据包对象
                DatagramPacket packet = new DatagramPacket(data, data.length);
                while(true) {
                    //读取数据到 缓冲区
                    server.receive(packet);
                    System.out.println("从:[" + packet.getSocketAddress() +"] 接收了数据!!!!!");
                    //输出客户端的 传递的信息
                    System.out.println("服务端接收的信息:" + new String(packet.getData(),"GBK"));
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    package com.woniuxy.java27.study;
    import java.io.UnsupportedEncodingException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetSocketAddress;
    public class UDPClientStudy {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            String str = "给高老师:今晚吃鸡!!!";
            //获得字符串的字节数组
            try {
                byte[] datas = str.getBytes("GBK");
                //定义数据包(并指定数据表,需要发送的地址)
                DatagramPacket packet = new DatagramPacket(datas,
                        datas.length, new InetSocketAddress("127.0.0.1", 9090));
                //定义一个Socket通道
                DatagramSocket socket = new DatagramSocket(5003);
                //发送内容
                socket.send(packet);
                System.out.println("发送成功!!!!");
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

4.2 面试题(Tcp/IP 与 UDP的区别)

1、TCP/IP过于依赖连接,UDP不是特别的依赖连接对象
2、TCP/IP在传输时,采用1问1答模式,它可以确保双向传输的数据,对方一定可以接收到,而UDP只管发,不在乎对方是否可以接收到
3、TCP/IP相对而言比较安全些,而UDP数据安全度较差