网络编程

一. 概述

  1. 什么叫计算机网络?

    是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来

    在网络操作系统网络管理软件网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

  2. 什么叫网络编程?

    就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。

二. 网络编程三要素

  1. IP地址

    ① 什么叫IP地址?

      每个设备在网络中的唯一标识,每台网络终端在网络中都有一个独立的地址,我们在网络中传输数据就是使用这个地址。

    ② cmd下对IP地址的操作:1. ipconfig:查看本机IP   2. ping:测试连接是否通 如: ping 192.168.40.62

  2. 端口号

    ① 什么叫端口号?

      每个程序在设备上的唯一标识,每个网络程序都需要绑定一个端口号,传输数据的时候除了确定发到哪台机器上(IP地址),还要明确发到哪个程序。

    ② 端口号范围从0-65535,编写网络应用就需要绑定一个端口号,尽量使用1024以上的,1024以下的基本上都被系统程序占用了。

      常用端口:1. mysql: 3306  2. oracle: 1521  3. tomcat: 8080  4. QQ: 4000

  3. 协议

    ① 什么叫协议?

      协议(通信协议)是为计算机网络中进行数据交换而建立的规则、标准或约定的集合

      通俗点说就是网络传输应该怎么做,受哪些约束

    ② 协议的分类

      国际组织定义了通用规则TCP/IP有两种传输方式:

      1. UDP传输

        ① 不需要事先建立连接,是不可靠协议

        ② 因无连接,速度快

        ③ 将数据及源地址和目的地址封装成数据包,每个数据报的大小限制在64k内

        如: 聊天,视频,桌面共享

      2. TCP传输

        ① 建立连接,形成传输数据的通道

        ② 通过三次握手完成连接,是可靠协议

        ③ 可以进行大数据传输

         ④ 必须建立连接,效率会骚低

        如 : 打电话拨号过程,下载  下面会详细讲解这两种方式

三. UDP传输

  1. 首先需要先了解几个在Java中的概念

    ① IP地址

      IP地址在Java中有对应的封装类—InetAddress

      1. 获取本地主机地址

        InetAddress i = InetAddress.getLocalHost();  // 得到本机IP对象 

        String name = i.getHostName();     // 得到主机名

        String address = i.getHostAddress();    // 得到IP地址

      2. 根据主机名获取获取InetAddress对象

        InetAddress[] ia = InetAddress.getAllByName("www.baidu.com");

        System.out.println("主机个数:"+ia.length);

    ② Socket套接字

      通信设备的两端都要有一个Socket才能进行通信,而数据在两个Socket间通过IO流传输。

      所以网络通信也可以说是在Socket间的通过IO流进行通信。(IO流比作船,Socket比作码头)

      因为网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字

      所以Socket在应用程序中创建,通过一种绑定机制与驱动程序建立关系,告诉自己所对应的IP和port。 

  2. 接收端和发送端

    ① 发送端

      1. DatagramSocket socket = new DatagramSocket();  //创建socket相当于创建码头(大众码头)

      解析: DatagramSocket是大众码头,不用指定参数,发到哪里去在货物上指明,所有人都能发

      而TCP传输中的Socket相当于是VIP码头,给专人使用的,所以Socket码头上参数就指明了连接指定的IP地址和端口号

      2. 创建DatagramPacket—货物,需指明数据,长度,发送地址和端口号

      new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666); 

      3. 使用DatagramSocket发送DatagramPacket

      socket.send(packet);

      4. 关闭DatagramSocket

      socket.close();

    ② 接收端

      1. DatagramSocket socket = new DatagramSocket(6666);      //创建socket相当于创建码头,需指明自己的端口号

      2. 创建DatagramPacket, 指定接收货物的容器,指明数组, 长度    

      DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);

      3. 使用DatagramSocket接收DatagramPacket

      socket.receive(packet); 

      因为定义的1024不一定就是发送过来货物的长度,所以一般需要通过packet在获得数组和长度

      byte[] arr = packet.getData();

      int len = packet.getLength();

      String ip = packet.getAddress().getHostAddress();

      System.out.println(ip + ":" + new String(arr,0,len));

      4. 服务端一般不需要关闭

四 TCP传输

  TCP与UDP的区别是每次传输都要用三次握手建立连接,而且区分客户端和服务端,http协议就是用TCP传输的

  1. 客户端

    ① 创建Socket,连接服务端(指定ip地址,端口号)通过ip地址找对应的服务器

    Socket socket = new Socket("127.0.0.1", 9999);  //创建Socket,(VIP码头)连接指定的IP地址和端口号

    ② 调用Socket的getInputStream()和getOutputStream()方法获取和服务端相连的IO流

    输入流可以读取服务端输出流写出的数据,输出流可以写出数据到服务端的输入流

  2. 服务端

    ① 创建ServerSocket(需要指定端口号)

    ServerSocket server = new ServerSocket(9999); //创建服务器

    ② 调用ServerSocket的accept()方法接收一个客户端请求,得到一个Socket

    Socket socket = server.accept();    //接受客户端的请求

    ③ 调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的IO流

    输入流可以读取客户端输出流写出的数据,输出流可以写出数据到客户端的输入流

    注意:服务器端一定要是多线程接收请求

  3. 例子:客户端上传文件到服务器端

  客户端代码

 

/**
 * 客户端
 * 步骤:
 * 1. 提示要输入的上传路径,检验是否是文件夹或者路径
 * 2. 文件存在,开启客户端,发送文件名到服务器
 * 3. 接收结果,如果已经存在给予"不必多次发送的提示",程序退出
 * 4. 如果不存在,将文件传输到服务器端
 * @author Administrator
 *
 */
public class ClientSocket {

    public static void main(String[] args) throws UnknownHostException, IOException {
        // 1. 提示要输入的上传路径,检验是否是文件夹或者路径
        File file = getFile();
        //2. 文件存在,开启客户端,发送文件名到服务器
        Socket client = new Socket("127.0.0.1",9999);
        BufferedReader bufr = new BufferedReader(new InputStreamReader(client.getInputStream()));
        PrintStream ps = new PrintStream(client.getOutputStream());
        
        ps.println(file.getName()); // 发送文件到服务器端
        // 3. 接收结果,如果已经存在给予"不必多次发送的提示",程序退出
        String result = bufr.readLine();
        if("存在".equals(result)){
            System.out.println("你上传的文件已经存在,请不要重复上传");
            client.close();
            return ;
        }
        // 4. 如果不存在,将文件传输到服务器端
        FileInputStream fis = new FileInputStream(file);
        byte[] arr =new byte[8192];
        for(int len; (len = fis.read(arr))!=-1;){
            ps.write(arr, 0, len);
        }
        System.out.println("上传成功");
        fis.close();
        client.close();
    }

    /**
     * 输入文件路径,判断是否存在,或者是文件夹
     * @return
     */
    private static File getFile() {
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入一个文件路径: ");
        while(true){
            String line = sc.nextLine();
            File file = new File(line);
            if(!file.exists()){
                System.out.println("文件不存在,请输入一个确切的文件路径:");
            }else if(file.isDirectory()){
                System.out.println("您录入的是文件夹路径,请输入一个确切的文件路径");
            }else{
                return file;
            }
        }
    }

}

 

   服务端代码:

/**
 * 服务器端(多线程) 
 * 1. 开启服务器,指定端口号 
 * 2. 接受请求,读取文件名 
 * 3. 判断文件是否存在,将结果发回
 * 4. 如果不存在,读取客户端发送来的数据
 * 
 * @author Administrator
 * 
 */
public class Server_Socket {

    public static void main(String[] args) throws IOException {
        // 1. 开启服务器,指定端口号(多线程)
        ServerSocket server = new ServerSocket(9999); // 指定端口号
        // 2. 接收请求,读取文件名
        while (true) {
            final Socket socket = server.accept(); // 得到客户端的请求
            new Thread(new Runnable() { // 开启一个子线程响应请求
                        @Override
                        public void run() {
                            try {
                                InputStream is = socket.getInputStream();
                                BufferedReader bufr = new BufferedReader(
                                        new InputStreamReader(
                                                is));
                                PrintStream ps = new PrintStream(socket.getOutputStream()); 
                                String fileName = bufr.readLine();
                                // 3. 判断文件名在服务器指定的目录下是否存在,将结果返回客户端
                                File dir = new File("client_file");
                                dir.mkdir();  //创建文件夹
                                File file = new File(dir,fileName);
                                if(file.exists()){
                                    ps.println("存在");
                                    socket.close();
                                    return ;
                                }else{
                                    ps.println("不存在");
                                    // 4. 如果不存在,读取客户端发送来的数据
                                    FileOutputStream fos = new FileOutputStream(file);
                                    byte[] arr = new byte[8192];
                                    for(int len; (len=is.read(arr))!=-1;){
                                        fos.write(arr, 0, len);
                                    }
                                    fos.close();
                                    socket.close();
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }) {

            }.start();

        }
    }

}