文章目录
- 网络数据传输的基本原理
 - UDP
 - 发送端步骤
 - 接收端步骤
 - DatagramSocket
 - DatagramPacket
 - 举例
 - 版本1:发送端发送消息,接收端接收并打印
 - 版本2:创建一个NetworkUtils工具类优化版本1
 - 版本3:发送端接收端相互发送
 - 版本4:使用多线程
 
- TCP
 - 客户端步骤
 - 服务端步骤
 - Socket
 - ServerSocket
 - 举例
 - 版本1:客户端发送消息,服务端接收并打印
 - 版本2:多个客户端发送,服务端接收(多线程处理)
 - 版本3:客户端发送对象(序列化),服务端接收
 - 版本4:客户端上传文件到服务端
 
网络数据传输的基本原理

- ip地址 
  
- 唯一确定目的主机
 
 - port 端口号 
  
- 唯一确定进程
 
 - 协议 
  
- UDP协议:无连接的不可靠的协议
 - TCP协议:面向连接的可靠的协议
 
 
UDP

发送端步骤
- 创建发送端的socket对象
 - 把要发送的数据封装成数据报包
 send方法发送数据报包- 释放资源
close 
接收端步骤
- 创建接收端的socket对象
 - 创建用于接收的数据报包
 receive方法接收数据- 解析数据报包
 - 释放资源
close 
DatagramSocket
此类表示用来发送和接收数据报包的套接字。
构造方法:
DatagramSocket(int port)     //  创建数据报套接字并将其绑定到本地主机上的指定端口。
 
成员方法:
 
DatagramPacket
此类表示数据报包。
构造方法
1. 用于发送的
DatagramPacket(byte[] buf,  int offset, int length, InetAddress address, int port)      
// 构造数据报包,用来将长度为 length 偏移量为 offset  的包发送到指定主机上的指定端口号
// InetAddress address ---> ip值
2. 用于接受的
DatagramPacket(byte[] buf,  int offset, int length)      
// 构造DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。
 
成员方法:

举例
版本1:发送端发送消息,接收端接收并打印
// 发送端sender
public class Sender {
    public static void main(String[] args) throws IOException {
        //   1. 创建发送端的socket对象
        DatagramSocket datagramSocket = new DatagramSocket(8888);
        //   2. 把要发送的数据封装成数据报包
        String s = "hello";
        // DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
        // 构造数据报包,用来将长度为 length 偏移量为 offset  的包发送到指定主机上的指定端口号
        byte[] bytes = s.getBytes();
        InetAddress targetIP = InetAddress.getByName("127.0.0.1");
        int port = 9999;
        DatagramPacket sendPacket =
                new DatagramPacket(bytes, 0, bytes.length, targetIP, port);
        //   3. send方法发送数据报包
        datagramSocket.send(sendPacket);
        //   4. 释放资源close
        datagramSocket.close();
    }
}
———————————————————————————————————————————————————————————————————————————————————————————
// 接收端receiver
public class Receiver {
    public static void main(String[] args) throws IOException {
        // 1. 创建接收端的socket对象
        DatagramSocket datagramSocket = new DatagramSocket(9999);
        // 2. 创建用于接收的数据报包
        // DatagramPacket(byte[] buf,  int offset, int length)
        // 构造DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。
        byte[] bytes = new byte[1024];
        DatagramPacket receivePacket = new DatagramPacket(bytes, 0, bytes.length);
        // 3. receive方法接收数据
        System.out.println("receiver before");
        datagramSocket.receive(receivePacket);
        System.out.println("receiver after");
        // 4. 解析数据报包
        byte[] data = receivePacket.getData();
        int length = receivePacket.getLength();
        int offset = receivePacket.getOffset();
        System.out.println(new String(data, offset, length));
        // 5. 释放资源close
        datagramSocket.close();
    }
}
 
版本2:创建一个NetworkUtils工具类优化版本1
NetworkUtil.java
public class NetworkUtils {
    // 提供一个获取发送数据报包的方法
    public static DatagramPacket getSendPacket(String msg, String ip, int port) 
    throws UnknownHostException {
        // 把数据封装到包里
        byte[] bytes = msg.getBytes();
        InetAddress targetIP = InetAddress.getByName(ip);
        DatagramPacket sendPacket = 
                new DatagramPacket(bytes, 0, bytes.length, targetIP, port);
        return sendPacket;
    }
    // 提供一个获取接受数据报包的方法
    public static DatagramPacket getReceivePacket() {
        // 创建接收数据报的包
        byte[] bytes = new byte[1024];
        DatagramPacket receivePacket = 
                new DatagramPacket(bytes, 0, bytes.length);
        return receivePacket;
    }
    // 提供一个解析数据报包的方法
    public static String parseMsg(DatagramPacket receivePacket) {
        byte[] bytes = receivePacket.getData();
        int length = receivePacket.getLength();
        int offset = receivePacket.getOffset();
        return new String(bytes,offset,length);
    }
}
————————————————————————————————————————————————————————————————————————————————————————————
// sender 发送端
public class Sender {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(8888);
        String s = "hello hi";
        DatagramPacket sendPacket = NetworkUtils.getSendPacket(s, "127.0.0.1", 9999);
        datagramSocket.send(sendPacket);
        datagramSocket.close();
    }
}
————————————————————————————————————————————————————————————————————————————————————————————
// 接收端receiver
public class Receiver {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(9999);
        DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
        datagramSocket.receive(receivePacket);
        String s = NetworkUtils.parseMsg(receivePacket);
        System.out.println(s);
        datagramSocket.close();
    }
}
 
版本3:发送端接收端相互发送
// 发送端sender
public class Sender {
    public static void main(String[] args) throws IOException {
        // 1. 创建发送端的socket对象
        DatagramSocket datagramSocket = new DatagramSocket(8888);
        // 2. 创建Scannner对象
        Scanner scanner = new Scanner(System.in);
        // while循环
        while (true) {
            // 3. 发送逻辑
            // 键盘接收数据
            String s = scanner.nextLine();
            // 把数据封装成数据包
            DatagramPacket sendPacket = NetworkUtils.getSendPacket(s, "127.0.0.1", 9999);
            // send
            datagramSocket.send(sendPacket);
            // 4. 接受逻辑
            // 创建用于接受的数据报包
            DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
            // receive方法接收
            datagramSocket.receive(receivePacket);
            // 解析
            String s1 = NetworkUtils.parseMsg(receivePacket);
            System.out.println("接收到了" + receivePacket.getSocketAddress() + "的消息" + s1);
        }
    }
}
// 接收端 receiver
public class Receiver {
    public static void main(String[] args) throws IOException {
        // 创建接收端的Socket对象
        DatagramSocket datagramSocket = new DatagramSocket(9999);
        // 创建Scanner对象
        Scanner scanner = new Scanner(System.in);
        // while循环
        while (true) {
            // 接受逻辑
            // 创建用于接受的数据报包
            DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
            // receive接受
            datagramSocket.receive(receivePacket);
            // parse解析
            String msg = NetworkUtils.parseMsg(receivePacket);
            System.out.println("接收到了" + receivePacket.getSocketAddress() + "的消息" + msg);
            // 发送逻辑
            // 键盘接收数据
            String s = scanner.nextLine();
            // 把数据封装成包
            DatagramPacket sendPacket = NetworkUtils.getSendPacket(s, "127.0.0.1", 8888);
            // send
            datagramSocket.send(sendPacket);
        }
    }
}
 
版本4:使用多线程

- 定义发送任务 SendTask 专门用来发送消息
 - 定义接收任务 receiveTask 专门用来接收消息
 
eg:
SendTask:
public class SenderTask implements Runnable {
    // DatagramSocket datagramSocket
    DatagramSocket datagramSocket;
    // String ip
    String ip;
    // int port
    int port;
    public SenderTask(DatagramSocket datagramSocket, String ip, int port) {
        this.datagramSocket = datagramSocket;
        this.ip = ip;
        this.port = port;
    }
    @Override
    public void run() {
        // 创建Scanner对象
        Scanner scanner = new Scanner(System.in);
        // while
        while(true){
            // 键盘输入消息
            String s = scanner.nextLine();
            // 把消息封装成数据报包
            try {
                DatagramPacket sendPacket = NetworkUtils.getSendPacket(s, ip, port);
                // send
                datagramSocket.send(sendPacket);
            } catch (UnknownHostException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
ReceiveTask:
public class ReceiveTask implements Runnable{
    // 成员变量
    DatagramSocket datagramSocket;
    public ReceiveTask(DatagramSocket datagramSocket) {
        this.datagramSocket = datagramSocket;
    }
    @Override
    public void run() {
        // 只是接收数据
        while(true){
            // 创建用于接收的数据报包
            DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
            // receive接收
            try {
                datagramSocket.receive(receivePacket);
                // 解析
                String s = NetworkUtils.parseMsg(receivePacket);
                // 打印
                System.out.println("s = " + s);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
OnePerson:
public class OnePerson {
    public static void main(String[] args) throws IOException {
        // 创建DatagramSocket对象
        DatagramSocket datagramSocket = new DatagramSocket(8888);
        // 创建发送线程 接收线程并启动
        new Thread(new SenderTask(datagramSocket,"127.0.0.1",9999)).start();
        // 创建接收线程
        new Thread(new ReceiveTask(datagramSocket)).start();
    }
}
AnotherPerson:
public class AnotherPerson {
    public static void main(String[] args) throws IOException {
        // 创建DatagramSocket对象
        DatagramSocket datagramSocket = new DatagramSocket(9999);
        // 创建发送线程 接收线程并启动
        new Thread(new SenderTask(datagramSocket, "127.0.0.1", 8888)).start();
        // 创建接收线程
        new Thread(new ReceiveTask(datagramSocket)).start();
    }
}
 
TCP

客户端步骤
-  
创建客户端Socket对象(
Socket) -  
从socket中获取输入输出流
 -  
利用输出输出流进行读写操作
 -  
释放资源
close 
服务端步骤
-  
创建服务端的socket对象(
ServerSocket) -  
通过
accept建立连接, 得到socket对象 -  
从socket中得到输入输出流
 -  
利用输入输出流进行读写操作
 -  
释放资源
 
Socket
此类实现客户端套接字
构造方法:
Socket(String host,  int port)       
// 创建一个流套接字并将其连接到指定主机上的指定端口号。
 
注:这里的host指的是目的主机的指定端口
成员方法:
ServerSocket
此类实现服务器套接字
构造方法:
ServerSocket(int port)        
// 创建绑定到特定端口的服务器套接字。
 
成员方法:
举例
版本1:客户端发送消息,服务端接收并打印
注意事项:
- 先启动服务端,后启动客户端
 - 端口号不能重复,如果重复占用的话,换一个端口号
 
eg:
客户端Client:
public class Client {
    public static void main(String[] args) throws IOException {
        // 1. 创建客户端Socket对象
        // Socket(String host,  int port)
        Socket socket = new Socket("127.0.0.1", 9999);
        // 2. 从socket中获取输入输出流
        OutputStream out = socket.getOutputStream();
        // 3. 利用输出输出流进行读写操作
        out.write("hello".getBytes());
        // 4. 释放资源close
        socket.close();
    }
}
服务端Server:
public class Server {
    public static void main(String[] args) throws IOException {
        // 1. 创建服务端的socket对象(ServerSocket)
        // ServerSocket(int port)
        ServerSocket serverSocket = new ServerSocket(9999);
        // 2. 通过accept建立连接, 得到socket对象
        Socket socket = serverSocket.accept();
        // 3. 从socket中得到输入输出流
        InputStream inputStream = socket.getInputStream();
        // 4. 利用输入输出流进行读写操作
        byte[] bytes = new byte[1024];
        int readCount = inputStream.read(bytes);
        System.out.println(new String(bytes, 0, readCount));
        // 5. 释放资源
        serverSocket.close();
        socket.close();
    }
}
 
版本2:多个客户端发送,服务端接收(多线程处理)
eg:
客户端Client:
public class Client {
    public static void main(String[] args) throws IOException {
        // 创建客户端的socket对象
        Socket socket = new Socket("127.0.0.1",9999);
        // 创建scanner对象
        Scanner scanner = new Scanner(System.in);
        // while循环
        while(true){
            String s = scanner.nextLine();
            // 从socket对象中获取输入输出流
            // 字节流
            OutputStream out = socket.getOutputStream();
            // 转换成字符流
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(out);
            // write
//            out.write(s.getBytes());
            outputStreamWriter.write(s);
            // flush
            outputStreamWriter.flush();
        }
    }
}
服务端Server:
public class Server {
    public static void main(String[] args) throws IOException {
        // 创建服务端的socket对象
        ServerSocket serverSocket = new ServerSocket(9999);
        // 创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(2);
        // while循环
        while (true){
            // 通过accept方法建立连接
            Socket socket = serverSocket.accept();
            // 向线程池中提交任务
            pool.submit(new ConnectTask(socket));
            // 使用多线程
            // new Thread(new ConnectTask(socket)).start();
        }
    }
}
// 多线程改进
class ConnectTask implements Runnable{
    // 定义成员变量
    Socket socket;
    public ConnectTask(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        // 读取数据
        while (true){
            try {
                InputStream in = socket.getInputStream();
                byte[] bytes = new byte[1024];
                int readCount = in.read(bytes);
                String s = new String(bytes, 0, readCount);
                System.out.println("接收到了来自" + socket.getLocalAddress()
                        + "端口号是:" + socket.getPort()
                        + "线程名是:" + Thread.currentThread().getName()
                        + ",内容是:" + s);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
 
版本3:客户端发送对象(序列化),服务端接收
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。
 可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
eg:
客户端Client:
public class Client {
    public static void main(String[] args) throws IOException {
        // 创建客户端socket对象
        Socket socket = new Socket("127.0.0.1", 12300);
        // 从socket中获取输出流
        OutputStream out = socket.getOutputStream();
        // 把输出流进行包装,包装成序列化流
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
        // 创建学生对象
        Student student = new Student("zs", 20);
        // writeObject
        objectOutputStream.writeObject(student);
        // close
        objectOutputStream.close();
        socket.close();
    }
}
服务端Server:
public class Server {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 创建客户端的socket对象
        ServerSocket serverSocket = new ServerSocket(12300);
        // accept方法建立连接,得到socket对象
        Socket socket = serverSocket.accept();
        // 得到输入流
        InputStream in = socket.getInputStream();
        // 把输入流进行包装 ObjectInputStream
        ObjectInputStream objectInputStream = new ObjectInputStream(in);
        // readObject
        Object o = objectInputStream.readObject();
        // 打印
        System.out.println(o);
        // close
        objectInputStream.close();
        socket.close();
        serverSocket.close();
    }
}
 
版本4:客户端上传文件到服务端

eg:
客户端Client:
// 客户端上传文件到服务端
public class Client {
    public static void main(String[] args) throws IOException {
        // 创建客户端的socket对象
        Socket socket = new Socket("127.0.0.1", 12345);
        // 创建自己的输入流对象
        FileInputStream fileInputStream = 
        	  new FileInputStream("D:\\Java_test\\testPhoto.jpg");
        // 通过自己的输入流读取文件信息
        OutputStream outputStream = socket.getOutputStream();
        // 通过socket获取输出流对象
        int readCount;
        byte[] bytes = new byte[1024];
        while ((readCount = fileInputStream.read(bytes)) != -1) {
            // 边读边写
            outputStream.write(bytes, 0, readCount);
        }
        // 释放资源
        fileInputStream.close();
        outputStream.close();
        socket.close();
    }
}
服务端Server:
public class Server {
    public static void main(String[] args) throws IOException {
        // 创建服务端的socket对象
        ServerSocket serverSocket = new ServerSocket(12345);
        // 创建自己的输出流对象
        FileOutputStream fileOutputStream = 
                new FileOutputStream("D:\\darkNight.jpg");
        // accept方法建立连接 得到socket对象
        Socket socket = serverSocket.accept();
        // 从socket中获取输入流
        InputStream in = socket.getInputStream();
        // 边读边写
        int readCount;
        byte[] bytes = new byte[1024];
        while ((readCount = in.read(bytes)) != -1) {
            fileOutputStream.write(bytes, 0, readCount);
        }
        // 释放
        serverSocket.close();
        socket.close();
        fileOutputStream.close();
        in.close();
    }
}
 

Socket的半关闭
版本4plus:
eg:
客户端Client:
public class Server {
    public static void main(String[] args) throws IOException {
        // 创建服务端的socket对象
        ServerSocket serverSocket = new ServerSocket(12345);
        // 创建自己的输出流对象
        FileOutputStream fileOutputStream =
                new FileOutputStream("D:\\darkNight.jpg");
        // accept方法建立连接 得到socket对象
        Socket socket = serverSocket.accept();
        // 从socket中获取输入流
        InputStream in = socket.getInputStream();
        // 边读边写
        int readCount;
        byte[] bytes = new byte[1024];
        while ((readCount = in.read(bytes)) != -1) {
            fileOutputStream.write(bytes, 0, readCount);
        }
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("文件上传成功".getBytes());
        // 释放
        serverSocket.close();
        socket.close();
        fileOutputStream.close();
        in.close();
    }
}
服务端Server:
public class Server {
    public static void main(String[] args) throws IOException {
        // 创建服务端的socket对象
        ServerSocket serverSocket = new ServerSocket(12345);
        // 创建自己的输出流对象
        FileOutputStream fileOutputStream =
                new FileOutputStream("D:\\darkNight.jpg");
        // accept方法建立连接 得到socket对象
        Socket socket = serverSocket.accept();
        // 从socket中获取输入流
        InputStream in = socket.getInputStream();
        // 边读边写
        int readCount;
        byte[] bytes = new byte[1024];
        while ((readCount = in.read(bytes)) != -1) {
            fileOutputStream.write(bytes, 0, readCount);
        }
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("文件上传成功".getBytes());
        // 释放
        serverSocket.close();
        socket.close();
        fileOutputStream.close();
        in.close();
    }
}
 








![街机模拟游戏逆向工程(HACKROM)教程:[1]数据的存储与读取](https://img-blog.csdnimg.cn/direct/3227b660010942019448656e45a8218c.png)












