day32-33周日周一休息_chap-Thread_1.多线程实现方式2.锁3.网络编程(本篇网络编程)

概念大纲:

1.套接字:一个类的对象,参数是目的ip和端口,用来建立连接

主要成员方法有发送和接收,socket这个单词就是一个标志

2.关于UDP(1)和TCP/IP(2)

(1)UDP协议:通过数据报包来传输

发送端

DatagramSocket和DatagramPacket

DatagramSocket类对象,指定自己的工作端口

DatagramPacket类对象,指定发送目的地ip和目的ip的工作端口

接收端

DatagramSocket和DatagramPacket

DatagramSocket类对象,指定自己的(接收)工作端口

DatagramPacket类对象,指定发送目的地ip和目的ip的工作端口

.getData()

.getOffset()

.getLength()

四个版本需求:(自动回复雏形)

(1)收发一个包

package com.cskaoyan.udp.v1;

import java.io.IOException;
import java.net.*;

/*
- 【1.】创建套接字Socket对象:指定发送端的工作端口
- 【2.】封装数据报包Packet对象:将要发送的数据(字节数组)、目的地ip、目的地工作端口号传入构造包方法
- 【3.】调用套接字Socket对象的发送方法
- 【4.】释放资源:套接字Socket对象调用close方法
 */
public class Sender {
    public static void main(String[] args) throws IOException {
        // 来自应用层的数据
        String s = "hello udp";

        // 建立udp的socket对象
        DatagramSocket datagramSocket = new DatagramSocket(8888);

        // 将要发送的数据封装成数据包
        // DatagramPacket(byte[] buf,  int offset,
        // int length, InetAddress address, int port)
        byte[] bytes = s.getBytes();
        InetAddress targetIP = InetAddress.getByName("127.0.0.1");
        int port = 9999;
        DatagramPacket packet =
                new DatagramPacket(bytes, 0, bytes.length, targetIP, port);

        // send(DatagramPacket p)
        // 从此套接字发送数据报包。
        datagramSocket.send(packet);
        // 释放资源
        datagramSocket.close();
    }
}
package com.cskaoyan.udp.v1;

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


/*
- 【1.】创建套接字Socket对象:指定接收端工作端口号
- 【2.】缓存数据报包Packet对象:将要接收的数据(字节数组)、开始索引、结束索引传入构造缓存包方法
- 【3.】调用套接字Socket对象的接收方法:
-       非常繁琐:getData方法
- 【4.】新加控制台输出:记录接收日志,输出到控制台
- 【5.】释放资源:没有调用,原因未知
 */

public class Receiver2 {
    public static void main(String[] args) throws Exception{
        // 创建接收端的socket对象
        DatagramSocket datagramSocket = new DatagramSocket(11111);
        while (true) {
            // 创建用于接收端的数据报包
            byte[] bytes = new byte[1024];
            DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
            // receive 并解析
            datagramSocket.receive(packet);
            byte[] data = packet.getData();
            int offset = packet.getOffset();
            int length = packet.getLength();
            String s = new String(data, offset, length);
            SocketAddress socketAddress = packet.getSocketAddress();
            System.out.println("接收到了来自 " +
                    socketAddress + " 的消息: " + s);
        }

        // close
    }
}

(2)while循环一直接收发

package com.cskaoyan.udp.v2;

import com.cskaoyan.utils.NetworkUtils;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Scanner;

/**
 * @description: 发送端
 * @author: 景天老师
 **/
/*
    要求重复发送
 */

/*
- 【1.】创建套接字Socket对象:指定发送端的工作端口 11111
- 【2.】创建控制台接收:直接通过System.in接收输入,作为传输数据字符串
- 【3.】封装数据报包Packet对象:目的地ip、目的地工作端口号、将要发送的数据(字节数组)传入构造包方法。注意:这里的getSenderPacket参数顺序跟之前是不一样的,之前是字节数组,要指定其实索引,而且在参数前几位;这里用字符串,在参数第三位
- 【4.】调用套接字Socket对象的发送方法
- 【5.】释放资源:没调用,原因未知
 */
public class Sender {
    public static void main(String[] args) throws Exception{
        // 创建发送端的socket对象
        DatagramSocket datagramSocket = new DatagramSocket(11111);
        Scanner scanner = new Scanner(System.in);
        while (true) {
            String s = scanner.nextLine();
            // 封装数据
            DatagramPacket senderPacket =
                    NetworkUtils.getSenderPacket("127.0.0.1", 12306, s);
            // send
            datagramSocket.send(senderPacket);
        }
        // close

    }
}
package com.cskaoyan.udp.v2;

import com.cskaoyan.utils.NetworkUtils;

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

/**
 * NetworkUtils包工具类,在这里要使用
 */

/*
- 【1.】创建套接字Socket对象:指定接收端的工作端口 11111
- 【2.】创建接收数据报包Packet对象:没有参数,查看NetworkUtils方法
- 【3.】调用数据报包的解析方法,得到信息字符串s
- 【4.】释放资源:没调用,原因未知
 */
public class Receiver {
    public static void main(String[] args) throws Exception{
        // 创建用于接收的socket对象
        DatagramSocket datagramSocket = new DatagramSocket(12306);

        while (true) {
            // 创建用于接收的数据报包
            DatagramPacket receiverPacket = NetworkUtils.getReceiverPacket();
            // 接收 并 解析 数据
            datagramSocket.receive(receiverPacket);
            String s = NetworkUtils.parsePacket(receiverPacket);
            System.out.println(s);
        }

        // close
    }
}

(3)发送端要可收可发,为了控制台获取反馈。接收端也是

package com.cskaoyan.udp.v3;

import com.cskaoyan.utils.NetworkUtils;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * @description: 发送端
 * @author: 景天老师
 **/

/*
    要求能够发送 也能够接收数据
 */

/**
 * NetworkUtils包工具类,在这里要使用
 *
 *  发送端:
 *        【1.】创建套接字对象
 *        【2.】接收键盘输入
 *        【3.】封装数据,发送
 *        【4.】接收数据,解析
 *        【5.】释放资源,数据报套接字关闭
 *
 *
 */
public class Sender {
    public static void main(String[] args) throws Exception{
        //【1.】创建用于发送的socket对象,指定自己的通信端口号 10000
        DatagramSocket datagramSocket = new DatagramSocket(10000);

        //【2.】接收键盘输入,用字节转字符包装,再用缓冲字符流包装
        BufferedReader br =
                new BufferedReader(new InputStreamReader(System.in));

        String line;//中介字符串声明在循环外
        while ((line = br.readLine()) != null) {
            //【3.】封装数据,发送
            //调用构造方法,生成一个发送包,传入目的ip、目的ip主机的通信端口号、信息字符串
            DatagramPacket senderPacket =
                    NetworkUtils.getSenderPacket("127.0.0.1", 10001, line);
            //用套接字发送
            datagramSocket.send(senderPacket);

            //【4.】接收数据,解析
            DatagramPacket receiverPacket = NetworkUtils.getReceiverPacket();//收到一个包
            datagramSocket.receive(receiverPacket);//数据报套接字接收,给成员变量赋值
            String s = NetworkUtils.parsePacket(receiverPacket);//解析出信息字符串
            System.out.println(s);//输出
            //结束匹配信号
            if (line.equals("88")) {
                break;
            }

        }
        //【5.】释放资源,数据报套接字关闭
        datagramSocket.close();
    }
}
package com.cskaoyan.udp.v3;

import com.cskaoyan.utils.NetworkUtils;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Scanner;

/**
 * @description: 接收端【控制台发送】
 * @author: 景天老师
 **/

/**
 * NetworkUtils包工具类,在这里要使用
 *
 *  接收端:
 *        【1.】创建套接字对象
 *        【2.】接收数据报包,解析
 *        【3.】生成发送包,发送
 *        【4.】释放资源
 *
 */

public class Receiver {
    public static void main(String[] args) throws IOException {
        //【1.】创建用于接收的socket对象,指定自己的通信端口号 10001
        DatagramSocket datagramSocket = new DatagramSocket(10001);
        //键盘录入【控制台输入信息,发送】
        Scanner scanner = new Scanner(System.in);

        while (true) {
            //【2.】接收数据,解析
            DatagramPacket receiverPacket = NetworkUtils.getReceiverPacket();
            datagramSocket.receive(receiverPacket);//数据报套接字接收,给成员变量赋值
            String s = NetworkUtils.parsePacket(receiverPacket);//解析出信息字符串
            System.out.println(s);

            //【3.】封装数据,发送
            String sendMsg = scanner.nextLine();//控制台输入信息,一行信息字符串
            //【4.】调用构造方法,生成一个发送包,传入目的ip、目的ip主机的通信端口号、信息字符串
            DatagramPacket senderPacket =
                    NetworkUtils.getSenderPacket("127.0.0.1", 10000, sendMsg);
            //用套接字发送
            datagramSocket.send(senderPacket);
            //结束匹配信号
            if (s.equals("88")) {
                break;
            }
        }
        //【5.】释放资源,数据报套接字关闭
        datagramSocket.close();
    }
}

(4)自动回复:自己创建一个Resource Bundle文件,命名随意,我这里写的是 datadict.properties

创建完,自己写自动回复的键值对:

先启动接收端,再启动发送端,不然会报错。关闭的时候随意

package com.cskaoyan.udp.v4;

import com.cskaoyan.utils.NetworkUtils;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Scanner;

/**
 * @description: 发送【实现自动回复功能】
 * @author: 景天老师
 **/

/*
    自动回复的机器人
 */

/**
 * NetworkUtils包工具类,在这里要使用
 *
 *  发送端:
 *        【1.】创建套接字对象
 *        【2.】接收键盘输入
 *        【3.】封装数据,发送
 *        【4.】接收数据,解析
 *        【5.】释放资源,数据报套接字关闭
 *
 *
 */
public class Sender {
    public static void main(String[] args) throws Exception{
        //【1.】创建发送端socket对象,指定自己的通信端口号 12345
        DatagramSocket datagramSocket = new DatagramSocket(12345);
        //【2.】接收键盘输入
        Scanner sc = new Scanner(System.in);//创建接收对象sc

        while (true) {
            String s = sc.nextLine();
            //【3.】封装数据,发送
            //调用构造方法,生成一个发送包,传入目的ip、目的ip主机的通信端口号、信息字符串
            DatagramPacket senderPacket =
                    NetworkUtils.getSenderPacket("127.0.0.1", 54321, s);
            //用套接字发送
            datagramSocket.send(senderPacket);

            //【4.】接收数据,解析
            DatagramPacket receiverPacket = NetworkUtils.getReceiverPacket();//收到一个包
            datagramSocket.receive(receiverPacket);//数据报套接字接收,给成员变量赋值
            String receiveMsg = NetworkUtils.parsePacket(receiverPacket);//解析出信息字符串
            //输出
            System.out.println(receiveMsg);
            //结束匹配信号
            if (s.equals("86")) {
                break;
            }
        }
        //【5.】释放资源,数据报套接字关闭
        datagramSocket.close();
    }
}
package com.cskaoyan.udp.v4;


import com.cskaoyan.utils.NetworkUtils;

import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Properties;

/**
 * @description: 接收端【实现自动回复功能】
 * @author: 景天老师
 **/

/**
 * NetworkUtils包工具类,在这里要使用
 *
 *  接收端:
 *        【1.】创建套接字对象
 *        【2.】加载自动回复键值对表
 *        【3.】接收数据报包
 *        【4.】匹配字典
 *        【5.】生成发送包
 *        【6.】释放资源
 *
 */

public class Receiver {
    public static void main(String[] args) throws Exception{
        //【1.】创建用于接收的socket对象,指定自己的通信端口号 54321
        DatagramSocket datagramSocket = new DatagramSocket(54321);
        //【2.自动回复库】创建properties对象
        Properties properties = new Properties();
        //load方法
        properties.load(new InputStreamReader(
                new FileInputStream("dataDict.properties"),"GBk"));

        while (true) {
            //【3.】接收数据,解析
            DatagramPacket receiverPacket = NetworkUtils.getReceiverPacket();//收到一个包
            datagramSocket.receive(receiverPacket);//数据报套接字接收,给成员变量赋值
            //这一步是在做什么
            String key = NetworkUtils.parsePacket(receiverPacket);//解析出键
            //【4.匹配字典】
            String responseMsg = properties.getProperty(key);//解析出值
            System.out.println(responseMsg);//输出接收到的消息
            if (responseMsg == null) {
                //如果没有匹配上  返回一个默认的字符串
                responseMsg = "正在忙,稍后回复";
            }
            //【5.】调用构造方法,生成一个发送包,传入目的ip、目的ip主机的通信端口号、信息字符串
            DatagramPacket senderPacket =
                    NetworkUtils.getSenderPacket("127.0.0.1", 12345, responseMsg);
            //用套接字发送
            datagramSocket.send(senderPacket);

            //结束匹配信号
            if (key.equals("86")) {
                break;
            }
        }
        //【6.】释放资源,数据报套接字关闭
        datagramSocket.close();
    }
}

3.工具类:

一个老师写的工具类,提取了接收、发送的方法,方便调用

package com.cskaoyan.utils;

import java.net.*;

/**
 * 网络的工具类,返回数据报包或者信息字符串
 * 【1.】getSenderPacket
 *      生成目的ip、目的ip通信端口、信息字符串的数据报包
 * 【2.】getReceiverPacket
 *      创建接收包
 * 【3.】parsePacket
 *      解析数据
 */

public class NetworkUtils {
    public static DatagramPacket getSenderPacket(String targetIp, int port, String message) throws UnknownHostException {
        // 创建用于发送的数据报包
        byte[] bytes = message.getBytes();
        InetAddress ip = InetAddress.getByName(targetIp);
        DatagramPacket packet =
                new DatagramPacket(bytes, 0, bytes.length, ip, port);//封装信息,生成数据报包
        return packet;
    }

    // 创建用于接收的数据报包
    public static DatagramPacket getReceiverPacket() {
        byte[] bytes = new byte[1024];//空数组,用来接收字节数据

        DatagramPacket packet =
                new DatagramPacket(bytes, 0, bytes.length);
        return packet;
    }

    // 解析数据
    public static String parsePacket(DatagramPacket packet){
        byte[] data = packet.getData();
        int offset = packet.getOffset();
        int length = packet.getLength();
        String s = new String(data, offset, length);//拼接字符串信息
        //SocketAddress socketAddress = packet.getSocketAddress();
        return s;
    }
}

(2)TCP/IP协议:通过io流来传输

客户端:Socket套接字

指定目的地ip和目的地工作端口号

 

服务端:ServerSocket套接字

指定自己的工作端口,调用监听方法来发现可能存在的连接请求,然后建立连接

三个版本需求:

(1)客户端、服务端没有反馈到控制台信息

package com.cskaoyan.tcp.v1;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

/**
 * @description: 客户端
 * @author: 景天老师
 **/

/*
- 建立客户端的Socket服务,并明确要连接的服务器。
- 如果对象建立成功,就表明已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入
- 根据需要从socket对象中获取输入,或输出流
- 向流中读取或写入数据
  释放资源
 */
public class Client {
    public static void main(String[] args) throws IOException {
        // 建立客户端的Socket服务,并明确要连接的服务器。
        // Socket(String host,  int port)
        Socket socket = new Socket("127.0.0.1", 8888);
        // 如果对象建立成功,就表明已经建立了数据传输的通道.
        // 就可以在该通道通过IO进行数据的读取和写入
        // 想服务端发送数据
        OutputStream out = socket.getOutputStream();
        out.write("hello tcp".getBytes());
        // 释放资源
        socket.close();
    }
}
package com.cskaoyan.tcp.v1;

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

/**
 * @description: 服务端
 * @author: 景天老师
 **/

/*
- 创建Serversocket对象,在指定端口,监听客户端连接请求
- 收到客户端连接请求后,建立Socket连接
- 如果连接建立成功,就表明已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入
- 从socket中根据需要获取输入,或输出流
  根据需要向流中写入数据或从流中读数据
  释放资源
 */
public class Server {
    public static void main(String[] args) throws Exception{
        // 创建Serversocket对象,在指定端口,监听客户端连接请求
        ServerSocket serverSocket = new ServerSocket(8888);
        // 收到客户端连接请求后,建立Socket连接
        Socket socket = serverSocket.accept();
        // 从socket中根据需要获取输入,或输出流
        InputStream in = socket.getInputStream();
        // 解析数据
        byte[] bytes = new byte[1024];
        int readCount = in.read(bytes);
        String s = new String(bytes, 0, readCount);
        System.out.println(s);
        socket.close();
        serverSocket.close();

    }
}

(2)服务端向客户端给出反馈,使用输出流,向客户端的控制台输出信息

package com.cskaoyan.tcp.v2;

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

/**
 * @description:
 * @author: 景天老师
 **/
/*
    客户端发送数据 服务端给出反馈
 */
public class Client {
    public static void main(String[] args) throws Exception{
        //【1.】建立客户端的Socket服务。参数:要连接的服务器ip地址,要连接的服务器的通信端口
        //Socket(String host,  int port)
        Socket socket = new Socket("127.0.0.1", 8888);
        //如果对象建立成功,就表明已经建立了数据传输的通道.
        //就可以在该通道通过IO进行数据的读取和写入
        //向服务端发送数据

        //【2.】创建输出流
        OutputStream out = socket.getOutputStream();
        out.write("你好".getBytes());//写入

        //【3.】接收来自服务器端的反馈
        //获取输入流,套接字服务对象调用.getInputStream方法
        InputStream in = socket.getInputStream();//这里接收的是什么,是服务器端的内容
        byte[] bytes = new byte[1024];
        int readCount = in.read(bytes);
        String s = new String(bytes, 0, readCount);
        System.out.println(s);

        //【4.】释放资源
        socket.close();
    }
}
package com.cskaoyan.tcp.v2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @description:
 * @author: 景天老师
 **/

public class Server {
    public static void main(String[] args) throws IOException {
        //【1.】创建服务端Serversocket对象,指定自己的通信端口 11111
        ServerSocket serverSocket = new ServerSocket(8888);

        //【2.】监听连接请求
        //监听到请求,就创建客户端套接字对象,存储监听到的套接字,后面调用套接字方法
        Socket socket = serverSocket.accept();//之后用这个客户端套接字来调用收发

        //【3.】用客户端套接字调用返回输入流方法,是服务器端读客户端内容到这个流
        InputStream in = socket.getInputStream();

        //读取中介:字节数组不能太小,也会有输入信息超出存储范围的情况
        byte[] bytes = new byte[1024];
        int readCount = in.read(bytes);
        //【4.】拼接信息字符串
        String s = new String(bytes, 0, readCount);
        System.out.println(s);//服务器端会输出客户端控制台的信息

        //【5.】给客户端一个反馈信息
        //创建客户端的信息输出流
        OutputStream out = socket.getOutputStream();
        out.write("还行".getBytes());//按下回车

        socket.close();//客户端关闭

        serverSocket.close();//服务端关闭
    }
}

改一下,写成while循环一直服务

package com.cskaoyan.tcp.v2;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @description:
 * @author: 景天老师
 **/

public class Server2 {
    public static void main(String[] args) throws Exception{
        //【1.】创建服务端Serversocket对象,指定自己的通信端口 11111
        ServerSocket serverSocket = new ServerSocket(11111);

        while (true) {
            //为什么这里不用字节数组,因为是字符流,我们在控制台输入汉字
            //byte[] bytes = new byte[1024];
            //int readCount = in.read(bytes);
            //String s = new String(bytes, 0, readCount);
            //【2.】监听连接请求
            //服务器端套接字一直用自己的 11111 端口监听
            //监听到请求,就创建客户端套接字对象,存储监听到的套接字,后面调用套接字方法
            Socket socket = serverSocket.accept();//之后用这个客户端套接字来调用收发

            //【3.】用客户端套接字调用返回输入流方法,是服务器端读客户端内容到这个流
            InputStream in = socket.getInputStream();

            //最内层是字节向字符转换
            //外层是缓冲字符流
            //需要理解这种嵌套定义的父类子类关系:
            //缓冲字符流,为了用读取行方法。参数是套接字返回的输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            //字符串变量s接收读取到的行,向服务器端输入
            String s = br.readLine();
            //返回客户端ip地址字符串
            InetAddress inetAddress = socket.getInetAddress();
            //返回客户端通信端口
            int port = socket.getPort();
            //【4.】拼接信息字符串
            System.out.println("接收到了来自" + inetAddress + ":" + port + "的消息: " + s);
        }

    }
}

(3)处理文件:需要确定两端的文件地址,然后还是处理字节流操作

package com.cskaoyan.tcp.v3;

import java.io.FileInputStream;
import java.io.FileReader;
import java.io.OutputStream;
import java.net.Socket;

/**
 * @description:
 * @author: 景天老师
 **/

// 把客户端文件上传到服务器
public class Client {
    public static void main(String[] args) throws Exception{
        //【1.】创建Socket对象,目的ip,目的通信端口
        Socket socket = new Socket("127.0.0.1", 11111);

        //【2.】读取本地文件,注意地址
        FileInputStream in = new FileInputStream("a.txt");

        //【3.】获取输出流
        OutputStream out = socket.getOutputStream();

        byte[] bytes = new byte[1024];
        int readCount;
        while ((readCount = in.read(bytes)) != -1) {
            out.write(bytes,0,readCount);
        }

        //【4.】释放资源
        in.close();
        socket.close();


    }
}
package com.cskaoyan.tcp.v3;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @description: 服务器端接收文件
 * @author: 景天老师
 **/

public class Server {
    public static void main(String[] args) throws Exception{
        //【1.】创建ServerSocket对象,指定通信端口 11111
        ServerSocket serverSocket = new ServerSocket(11111);

        //【2.】监听连接请求
        Socket socket = serverSocket.accept();

        //【3.】创建输出流对象
        FileOutputStream out = new FileOutputStream("b.txt");

        //【4.】客户端套接字传入读取内容,输入流
        InputStream in = socket.getInputStream();

        int readCount;
        byte[] bytes = new byte[1024];
        while ((readCount = in.read(bytes)) != -1) {
            //【5.】每读取1024字节到中间数组bytes成功,就把这些内容写入服务端输出流
            out.write(bytes,0,readCount);
        }

        //【6.】释放资源
        out.close();
        socket.close();
        serverSocket.close();
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值