网络编程【java全端课】

67 阅读6分钟

一、网络编程(了解)

1.1 网络编程的基本知识

1、IP

2、端口号

3、网络协议

1.2 Socket

无论是发送端还是接收端,无论是TCP还是UDP,都必须有Socket对象。Socket对象是Java中负责与底层的网卡驱动交互的对象。

  • UDP协议:DatagramSocket类
  • TCP协议:ServerSocket类和Socket类

1.3 基于UDP协议进行上层开发

3.3.1 发送端

基于UDP协议的发送端,在发送数据之前不会检查对方在不在,能不能成功传输数据,只管发送。

package com.mytest.net;

import java.net.*;

public class Send {//发送端

    public static void main(String[] args) throws Exception {
        //(1)创建一个DatagramSocket对象
        DatagramSocket ds = new DatagramSocket();

        //(2)准备要发送的消息
        String str = "马上要吃饭了,大家吃什么?";

        //(3)把数据打包,打包成 数据报格式
        //需要创建DatagramPacket对象
        byte[] data = str.getBytes();//把字符串转为字节输出,才能在网络中传输
//        InetAddress ip = InetAddress.getByName("www.123vwu.com");//基于域名的方式
        byte[] address = {(byte)192,(byte)168,36,21};
        /*
        192的二进制:
            int类型:00000000 00000000 00000000 11000000
            byte类型:11000000  IP地址是没有负数的,因为底层最高位仍然是数据位,不是符号位
                                                因为底层又把这个byte转为int

         */
        InetAddress ip = InetAddress.getByAddress(address);//基于IP地址的方式
        int port = 8888;//双方约定
        DatagramPacket dp = new DatagramPacket(data,0,data.length,ip,8888);

        //(4)发送
        //通过socket发送
        //数据流向: 程序中byte[]数组 -> dp包裹 -> ds(socket对象) -> 接收端
        ds.send(dp);

        //(5)关闭
        ds.close();
    }

}

3.3.2 接收端

先运行接收端,才能确保消息能收到。

package com.mytest.net;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class Receiver {//接收端

    public static void main(String[] args) throws Exception{
        //(1)创建一个DatagramSocket对象
        //告诉网卡驱动,网卡驱动程序需要在8888监听传给Receiver程序的数据
        DatagramSocket ds = new DatagramSocket(8888);

        //(2)准备一个字节数组,用来装接收到的数据
        byte[] data = new byte[1024];

        //(3)准备一个DatagramPacket对象,用于接收对方的数据报报文等信息
        //这里没有写IP地址,因为不用限制谁给我发,都可以给我发,只要对方发的时候,写明我的IP地址和端口号是8888就可以
        DatagramPacket dp = new DatagramPacket(data,0,data.length);
        
        //(4)接收
        ds.receive(dp);//如果没有人给你发消息,这句代码会阻塞等待
        
        //(5)查看消息
        int len = dp.getLength();//实际接收了几个字节
        System.out.println(new String(data,0,len));

        //(6)断开
        ds.close();
    }
}

3.4 基于TCP协议进行上层开发

基于TCP协议的网络应用程序必须分为服务器端和客户端。是C/S结构,C是Client,S是Server。

3.4.1 案例

需求:

  • 服务器开启,监听和等待客户端的连接
  • 客户端连接服务器
  • 服务器监听到客户端连接之后,给客户端发一句话:欢迎登录
  • 客户端接收服务器发送的消息
1、服务器端
package com.mytest.tcp1;

import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class Server1 {
    public static void main(String[] args) throws Exception{
        //(1)创建ServerSocket对象,用于监听和等待客户端的连接,它不负责传输数据。
        ServerSocket server = new ServerSocket(8888);

        Socket socket = server.accept();//如果没有客户端连接,这句代码会阻塞
        InetAddress clientIP = socket.getInetAddress();
        System.out.println(clientIP.getHostAddress() +"连接服务器成功!");

        //(2)准备发送的消息
        String str = "欢迎登录";
        byte[] data = str.getBytes();//如果没有指定具体的编码方式,就按照IDEA运行环境的编码处理UTF-8

        //(3)TCP协议要传输数据,需要用到字节流
        OutputStream out = socket.getOutputStream();
        //数据的流向:  服务器端程序 data数组-> out输出流 -> socket -> 网络 -> 客户端程序
        out.write(data);//输出整个字节数组

        //(4)关闭
        out.close();
        socket.close();
        server.close();
    }
}
2、客户端
package com.mytest.tcp1;

import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;

public class Client1 {
    public static void main(String[] args) throws Exception{
        //(1)创建一个Socket对象,用于连接服务器
        //必须指定服务器的IP地址和端口号
        byte[] address = {(byte)192,(byte)168,36,21};
        InetAddress ip = InetAddress.getByAddress(address);//基于IP地址的方式
        int port = 8888;//双方约定
        Socket socket = new Socket(ip,port);

        //(2)接收服务器端的消息,TCP协议要传输数据,需要用到字节流
        InputStream in = socket.getInputStream();
        byte[] data = new byte[1024];
        while(true){
            int len = in.read(data);
            if(len == -1){
                break;
            }
            System.out.println(new String(data,0,len));
        }

        //(3)关闭
        in.close();
        socket.close();

    }
}

3.4.4 案例

需求:

服务器:

  • 监听和等待客户端的连接
  • 接收客户端上传的文件,保存到服务器指定目录下,例如:服务器端D:\upload文件夹下
  • 接收完文件之后给客户端反馈:xxx文件上传成功!如果中途出现异常,反馈:xxx文件上传失败!

客户端:

  • 从键盘输入要上传的本地文件的完整路径值

  • 将指定的文件上传到服务器

  • 接收上传结果

  • 可以多个客户端同时上传

1、服务器端
package com.mytest.tcp4;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server4 {
    public static void main(String[] args)throws Exception {
        //(1)创建ServerSocket的对象,监听和接收客户端的连接
        ServerSocket server = new ServerSocket(8888);

        while(true) {
            Socket socket = server.accept();//它只要执行了,就代表一个客户端连接成功了

            UploadThread thread =new UploadThread(socket);
            thread.start();
        }
    }

    private static class UploadThread extends Thread{
        private Socket socket;

        public UploadThread(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try(
                    //接收(1)文件名.扩展名(2)文件内容
                    InputStream in = socket.getInputStream();
                    BufferedInputStream bis = new BufferedInputStream(in);//提高效率
                    ObjectInputStream ois = new ObjectInputStream(bis);

                    OutputStream out = socket.getOutputStream();
                    PrintStream ps = new PrintStream(out);
            ){

                //先接收(1)文件名.扩展名
                String filename = ois.readUTF();

                String clientIP = socket.getInetAddress().getHostAddress();
                String newFilename = System.currentTimeMillis() + "_" + clientIP + "_" + filename;

                //再接收(2)文件内容
                //数据的流向:客户端 -> in -> bis -> ois -> data -> bos->fos-> 服务器本地的d:\\upload文件夹下
                boolean flag = true;
                try(FileOutputStream fos = new FileOutputStream("d:\\upload\\"+newFilename);
                BufferedOutputStream bos = new BufferedOutputStream(fos);//提高效率
                ) {
                    byte[] data = new byte[1024];
                    while (true) {
                        int len = ois.read(data);//从客户端接收文件内容
                        if (len == -1) {
                            break;
                        }
                        bos.write(data, 0, len);//写到服务器的本地文件中
                    }
                }catch (Exception e){
                    flag = false;
                }
                ps.println(flag ? filename+"文件上传成功!" : filename+"文件上传失败!");
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}
2、客户端
package com.mytest.tcp4;

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

public class Client4 {
    public static void main(String[] args) throws Exception{
        //(1)创建Socket对象,因为只有他才可以建立连接,与网卡驱动交互,进行数据的传输
        //这里填写的是服务器端的IP地址和端口号。
        Socket socket = new Socket("192.168.36.21",8888);

        //(2)键盘输入完整的文件路径值
        Scanner keyboard = new Scanner(System.in);
        System.out.print("请输入你要上传的文件的完整路径值:");
        String filepath = keyboard.nextLine();//因为可能目录或文件名中包含空格
        //D:\temp\img\dog.jpg
        File file = new File(filepath);

        //(3)把这个文件上传给服务器
        OutputStream out = socket.getOutputStream();
        //分别上传(1)文件名.后缀名  和 (2)文件内容
        //为了服务器端方便区分是(1)文件名.后缀名  和 (2)文件内容,这里用ObjectOutputStream来传输数据
        BufferedOutputStream bos = new BufferedOutputStream(out);//提高效率
        ObjectOutputStream oos = new ObjectOutputStream(bos);

        String filename = file.getName();//得到文件名
        oos.writeUTF(filename);//对方用 ObjectInputStream流的readUTF()读取

        //从本地读取文件内容,再发送给服务器
        FileInputStream fis = new FileInputStream(filepath);
        BufferedInputStream bis = new BufferedInputStream(fis);//提高效率
        byte[] data = new byte[1024];
        //数据流向: filepath对应的本地文件 -> fis -> bis -> data数组 -> oos -> bos -> out -> 网络中 -> 服务器
        while(true){
            int len = bis.read(data);//从本地文件读取内容
            if(len==-1){
                break;
            }
            oos.write(data,0,len);//发送给服务器
        }
        oos.flush();
        socket.shutdownOutput();//关闭输出通道,但是连接保留
//        socket.close(); 或  oos.close(); //会导致连接断开

        //接收服务器反馈的结果,约定按行读取
        InputStream in = socket.getInputStream();
        Scanner netInput = new Scanner(in);
        while(netInput.hasNextLine()){
            String result = netInput.nextLine();
            System.out.println("结果:" + result);
        }

        netInput.close();
        in.close();
        bis.close();
        fis.close();
        oos.close();
        bos.close();
        out.close();
        keyboard.close();
        socket.close();
    }
}