[Java学习日记] 网络编程拓展

目录

一.使用TCP协议进行多发多收

二.实现服务端对客户端消息的接收与反馈

三.上传文件案例

四.接收多用户上传文件案例

五.线程池优化上传文件案例

六.控制台版聊天室(TCP协议)


一.使用TCP协议进行多发多收

使用TCP协议进行多发多收
1.在这个案例中,需要如何创建输入输出流?
使用的套接字对象与io流只用创建一次就行,通过循环进行多发多收
2.在这个案例中如何设置停止条件呢?
当客户端与服务器断开连接时(发送886),服务端的读取也会结束
3.如何让接收端(服务端)使用字符缓冲流的readline方法接收数据?
4.在这个案例中,如果使用缓冲流发送数据应该注意什么?
public class Demo341_Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 10086);
        OutputStream os = socket.getOutputStream();
        while (true) {
            String str = new Scanner(System.in).nextLine();
            //3.这里加一个换行方便readline读取数据,或者是用其他方法显示换行
            //4.在这里不使用缓冲流的write字符串方法,无法及时发出去..除非自己刷新
            os.write((str+"\n").getBytes());
            if ("886".equals(str)) break;
        }
        socket.close();
    }
}
1.如何启动服务器之后通过浏览器访问?
在这里只启动这服务器,然后浏览器输入127.0.0.1:10086就能访问
2.在此处的输入流接收到一条数据之后没有发送消息不会断开连接,为什么?
public class Demo341_Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10086);
        Socket socket = ss.accept();
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String str;
        System.out.println("2.在服务端的Reader中,读不到数据理应停止,但是这里却不是————只要有连接就能一直读:");
        System.out.println("服务端获取到的字节输入流可能会出现没有数据的情况。此时调用的read()方法会出现阻塞,也就是会一直等待直到数据可读。");
        while ((str = reader.readLine()) != null)System.out.println(str);
        socket.close();
        ss.close();
    }
}


二.实现服务端对客户端消息的接收与反馈

问题:关掉由socket获取的io流时也会关闭socket,如何解决?
可以使用socket类中的shutdown...方法关闭输入或者输出流
public class Demo342_Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 10086);
        socket.getOutputStream().write("客户端向服务端写数据!".getBytes());
        socket.shutdownOutput();
        System.out.println(new String(socket.getInputStream().readAllBytes()));
        socket.close();
    }
}
public class Demo342_Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10086);
        Socket socket = ss.accept();
        System.out.println(new String(socket.getInputStream().readAllBytes()));
        socket.shutdownInput();
        socket.getOutputStream().write("服务端向客户端返回数据!".getBytes());
        socket.close();
        ss.close();
    }
}

[Java学习日记] 网络编程拓展_第1张图片


三.上传文件案例

public class Demo343_Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 10086);

        OutputStream os = socket.getOutputStream();
        FileInputStream fis = new FileInputStream("C:\\Users\\33428\\Videos\\haniwa\\いつだって戦ってる.mp4");
        int len;
        byte[] bytes  = new byte[1024];
        while ((len = fis.read(bytes))!=-1)os.write(bytes,0,len);
        fis.close();

        socket.shutdownOutput();
        System.out.println(new String(socket.getInputStream().readAllBytes()));
        socket.close();
    }
}
类UUID:可以生成随机唯一的字符串,字符串内容唯一
如何使用UUID获取随机字符串?
public class Demo343_Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10086);
        Socket socket = ss.accept();

        InputStream is = socket.getInputStream();
        System.out.println("使用UUID的静态方法randomUUID,变成字符串再替换掉-即可生成随机字符串,由字母数字组成");
        String fileName = UUID.randomUUID().toString().replace("-","");
        FileOutputStream fos = new FileOutputStream("C:\\Users\\33428\\Desktop\\" +fileName+".mp4");
        byte[] bytes = new byte[1024];
        int len;
        while ((len = is.read(bytes))!=-1)fos.write(bytes,0,len);
        fos.close();

        socket.shutdownInput();
        socket.getOutputStream().write("服务端已经接收到所有数据!".getBytes());
        socket.close();
        ss.close();
    }
}

 


四.接收多用户上传文件案例

接收多用户上传文件案例
1.如果想要接受多个用户的上传的文件,应该怎么办呢?
循环是不行的:只能一个个的处理用户,这里使用了多线程
2.应该在哪个地方截取多线程的代码?
循环accept接收部分:每接收一个用户,都开启一条线程
public class Demo344_Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 10086);

        OutputStream os = socket.getOutputStream();
        FileInputStream fis = new FileInputStream("C:\\Users\\33428\\Videos\\haniwa\\いつだって戦ってる.mp4");
        int len;
        byte[] bytes  = new byte[256];
        while ((len = fis.read(bytes))!=-1)os.write(bytes,0,len);
        fis.close();

        socket.shutdownOutput();
        System.out.println(new String(socket.getInputStream().readAllBytes()));
        socket.close();
    }
}
public class Demo344_Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10086);
        while (true) {
            Socket socket = ss.accept();
            new Demo344_Thread(socket).start();
        }
    }
}
class Demo344_Thread extends Thread {
    Socket socket;
    public Demo344_Thread(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            String fileName = UUID.randomUUID().toString().replace("-","");
            FileOutputStream fos = new FileOutputStream("C:\\Users\\33428\\Desktop\\" +fileName+".mp4");
            byte[] bytes = new byte[256];
            int len;
            while ((len = is.read(bytes)) != -1) fos.write(bytes, 0, len);
            fos.close();
            socket.shutdownInput();
            socket.getOutputStream().write("服务端已经接收到所有数据!".getBytes());
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


五.线程池优化上传文件案例

public class Demo345_Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 10086);

        OutputStream os = socket.getOutputStream();
        FileInputStream fis = new FileInputStream("C:\\Users\\33428\\Videos\\haniwa\\いつだって戦ってる.mp4");
        int len;
        byte[] bytes  = new byte[128];
        while ((len = fis.read(bytes))!=-1)os.write(bytes,0,len);
        fis.close();

        socket.shutdownOutput();
        System.out.println(new String(socket.getInputStream().readAllBytes()));
        socket.close();
    }
}
public class Demo345_Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10086);
        ThreadPoolExecutor pool = new ThreadPoolExecutor(3,8,10,
                TimeUnit.MINUTES, new ArrayBlockingQueue<>(9),Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        while (true) {
            Socket socket = ss.accept();
            pool.submit(new Demo345_Runnable(socket));
        }
    }
}
class Demo345_Runnable implements Runnable {
    Socket socket;
    public Demo345_Runnable(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            String fileName = UUID.randomUUID().toString().replace("-","");
            FileOutputStream fos = new FileOutputStream("C:\\Users\\33428\\Desktop\\" +fileName+".mp4");
            byte[] bytes = new byte[256];
            int len;
            while ((len = is.read(bytes)) != -1) fos.write(bytes, 0, len);
            fos.close();
            socket.shutdownInput();
            socket.getOutputStream().write("服务端已经接收到所有数据!".getBytes());
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

六.控制台版聊天室(TCP协议)

没必要的数据就无需去放在服务器里面:放在客户端即可!
服务器就做一个最基本的接收-处理-发送
public class Client {
    public static void main(String[] args) throws IOException {
        //建立连接,开始运行,其实可以写到一个类里面
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 10000);
        MyRunnableForClient runnable = new MyRunnableForClient(socket);
        runnable.run();
    }
}
public class Server {
    //定义路径,定义在这里好修改一点
    static String STR_PATH = "JAVA基础\\src\\Day34_InternetPractice\\homework\\userInfo.properties";
    public static void main(String[] args) throws IOException {
        //定义服务套接字,创建线程池,接收连接并提交任务
        ServerSocket serverSocket = new ServerSocket(10000);
        ExecutorService pool = Executors.newFixedThreadPool(10);
        while (true) {
            Socket socket = serverSocket.accept();
            pool.submit(new MyRunnableForServer(socket));
        }
    }
}
1.多次使用对同一个套接字使用socket.getInputStream()获得的是同一个对象吗?
这里需要注意这样获得的对象不是同一个对象,所以我这里就只用了一个输入流,一个输出流
2.如何一起表示多行字符串?
使用文本块"""文本内容"""即可换行(JDK15引入)不用一个个加换行辣!!!
public class MyRunnableForClient {
    //提前定义需要用到的变量
    Socket socket;
    BufferedReader br;
    BufferedWriter bw;
    Scanner scanner;

    public MyRunnableForClient(Socket socket) {
        //在构造方法里面初始化上面的变量
        this.socket = socket;
        scanner = new Scanner(System.in);
        try {
            //多次定义br会有不同的对象地址
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void run() throws IOException {
        System.out.println("服务器启动成功");
        while (true) {
            //文本块
            System.out.println("""
                    ======欢迎来到聊天室======
                    1.登录
                    2.注册
                    请输入您的选择""");
            //在这里输入不同的数字进行选择,返回输入的数字
            if ("2".equals(chooseNum())) {
                //选择2则代表注册
                sendRegInfo();
                //返回的信息状态代表不同的注册情况
                if ("1".equals(br.readLine())) System.out.println("注册成功");
                else System.out.println("注册失败!用户名重复");
                continue;
            }
            //用户选择了登录
            login();
            //返回登录状态,做出不同回应
            String num = br.readLine();
            if ("2".equals(num)) System.out.println("密码有误");
            else if ("3".equals(num)) System.out.println("用户名不存在");
            else break;
        }
        System.out.println("登录成功");
        //在发送信息前提前开启一个接收信息的线程
        new Thread(() -> {//基本上一模一样,除了输入输出流对象
            try {
                while (true) System.out.println(br.readLine());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
        while (true) send(scanner.nextLine());
    }

    //循环输入,直到正确为止,发送选择并且返回输入的字符串
    private String chooseNum() throws IOException {
        String str;
        while (true) {
            str = scanner.nextLine();
            if ("1".equals(str) || "2".equals(str)) break;
            System.out.println("请输入正确的数字!");
        }
        send(str);
        return str;
    }

    //循环发送注册信息:直到正确为止
    private void sendRegInfo() throws IOException {
        while (true) {
            System.out.println("请输入用户名");
            String username = scanner.nextLine();
            System.out.println("请选择密码");
            String password = scanner.nextLine();
            if (username.matches("[a-zA-Z]{6,18}") && password.matches("[a-zA-Z][0-9]{2,7}")) {
                send("username=" + username + "&" + "password=" + password);
                break;
            }
            System.out.println("输入格式错误!");
        }
    }

    //登录选择信息即可,不需要校验
    private void login() throws IOException {
        System.out.println("请输入用户名");
        String username = scanner.nextLine();
        System.out.println("请输入密码");
        String password = scanner.nextLine();
        send("username=" + username + "&" + "password=" + password);
    }

    private void send(String str) throws IOException {
        bw.write(str + "\n");
        bw.flush();
    }
}
/*
properties也能够去接收txt文件
CSDN上面看到的:我这里就只创建一个输入输出流对象,这里可以了解一下
关于输入输出流的顺序与对象个数以后可以实验一下
服务器Socket和客户端Socket可以创建多个输入输出流对象,但是两端创建的个数必须保持对应
即通过客户端Socket创建多少个输入输出流对象,对应的服务器端的ServerSocket
通过accept()方法接收到Socket也必须创建多少个输入输出流对象。
*/
public class MyRunnableForServer implements Runnable {
    //定义输出流集合:这需要是同一个对象:每一个连接都需要这个集合,故为静态代码,其实定义在这里也只是为了少传一个参数,初始化
    static ArrayList arrayList= new ArrayList<>();

    //四个常用的变量:套接字连接,输入,输出,键盘输入
    Socket socket;
    BufferedWriter bw;
    BufferedReader br;
    Scanner scanner;
    //用户信息,登陆的用户名
    Properties properties;
    String name;

    public MyRunnableForServer(Socket socket) {
        //每创建一个对象都要初始化上面的七个变量
        this.socket = socket;
        scanner = new Scanner(System.in);
        properties = new Properties();
        try {
            properties.load(new FileInputStream(Server.STR_PATH));
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        try {
            //接收数据,这一块用户用户的登录与注册
            while (true) {
                //用户选择注册
                if ("2".equals(br.readLine())) {
                    receiveUserReg();
                    continue;
                }
                //用户选择登录
                if (receiveUserInfo()) break;
            }
            //开始聊天了:接收数据,然后转发给所有人
            //先把能聊天的加入集合
            arrayList.add(bw);
            while (true) {
                //接收客户端的数据,接收到了再发
                String str = br.readLine();
                //接收到信息直接遍历字符输出流:用输出流去发消息
                for (BufferedWriter writer : arrayList) {
                    writer.write(name + "说:" + str + "\n");
                    writer.flush();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //接收用户数据,返回注册数据:成功或者失败:默认发过来的数据是符合规范的
    private void receiveUserReg() throws IOException {
        String userInfo = br.readLine();
        String username = userInfo.split("&")[0].replace("username=", "");
        String password = userInfo.split("&")[1].replace("password=", "");
        //2代表用户名已存在,注册失败
        if (properties.containsKey(username)) {
            send("2");
            return;
        }
        //1代表可以注册,更新文件中的数据,也把数据放到当前所有用户的集合当中
        properties.put(username, password);
        properties.store(new FileWriter(Server.STR_PATH), "");
        send("1");
    }

    //用户登录并判断,返回true环开始聊天打破循,返回false继续循环
    //发给客户端状态码
    private boolean receiveUserInfo() throws IOException {
        String userInfo = br.readLine();
        String username = userInfo.split("&")[0].replace("username=", "");
        String password = userInfo.split("&")[1].replace("password=", "");
        //在这里可以传入字符串进行判断:一开始写错了,写成了contains,注意方法名
        if (properties.containsKey(username)) {
            if (properties.get(username).equals(password)) {
                //包含用户名且密码正确返回状态码1,返回ture
                send("1");
                name = username;
                return true;
                //其他根据情况返回状态码,返回false
            } else send("2");
        } else send("3");
        return false;
    }
    //这里由于是缓冲流:需要刷新,然后接收方才能有输出
    private void send(String str) throws IOException {
        bw.write(str + "\n");
        bw.flush();
    }
}

 [Java学习日记] 网络编程拓展_第2张图片

 [Java学习日记] 网络编程拓展_第3张图片

你可能感兴趣的:(JAVA黑马程序员笔记分享,学习,java)