什么是网络编程

c9e6b5e1ae8b447a9e843f3b2cf7c2bf.png

BS和CS 

6d6d9b00520e4fee81fac17c474bea2e.png

5679574523ca47b7ba439ee81b356112.png

0947dfb8855e4c4ebc3b7a1b402f0029.png

3db368c9c45a4fdd9d859b0eda6974f9.png

网络编程三要素 

942f6bfe165c4d6582c66210e6b21bd0.png

IP 

2d156ac6af084410b55799012939a87f.png

c4ed8cd5be82406fb2ccae7368fd1cda.png

964d1139d6ee4607b7341f8131d58892.png

IPv6还不够普及,那么目前如何解决ip地址不够用的问题呢 

90501da9bf7b458bb16349b9ef52d560.png

0c3c6220d1e749d8a993b19ff76e4ac1.png

42bce4fab8834615b26df4c3c41fbfb2.png

 端口号

afd3bfc3986447bd8fc227e69fd2ba2a.png

协议

54c86f985d864852bf914df57112970f.png

10b67c4085634c2081d0c8378c5b7737.png

6b394088ed48413f8f9197898236c49d.png

UDP通信程序

发送数据

56147f6d087547dc8803b83f724e23cf.png

package com.company.net;

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

public class UDPsendMessage {
    public static void main(String[] args) throws IOException {
        //1.创建DatagramSocket对象
        /**
         * 绑定端口,以后我们就通过这个端口往外发送数据
         * 空参:所有可用的端口中随机一个进行使用
         * 有参:指定端口号进行绑定
         */
        DatagramSocket ds = new DatagramSocket();
        //2.打包数据
        String str = "你好啊";
        byte[] bytes = str.getBytes();
        InetAddress address = InetAddress.getByName("127.0.0.1");
        int port = 10086;
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
        //3.发送数据
        ds.send(dp);
        //4.释放资源
        ds.close();
    }
}

 接收数据

d295b298a1af4f818edb75be82fd45f7.png

package com.company.net;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPReceiveMessage {
    public static void main(String[] args) throws IOException {
        //1.创建DatagramSocket对象
        /**
         * 接收的时候一定要绑定端口
         * 绑定的端口和发送的一致
         */
        DatagramSocket ds = new DatagramSocket(10086);
        //2.接收数据包
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
        //该方法是阻塞的,等待发送端发送消息
        ds.receive(dp);
        //3.解析数据包
        byte[] data = dp.getData();
        int len = dp.getLength();
        InetAddress address = dp.getAddress();
        int port = dp.getPort();

        System.out.println("接收到的数据"+new String(data,0,len));
        System.out.println("该数据是从"+address+"这台电脑的"+port+"端口发送的");
        //4.释放资源
        ds.close();
    }
}

 先运行接收数据代码、在运行发送数据代码

f81ab7c2f3c64a7ebcf5d6ee61d9ee55.png

 聊天室

319084bd231b4beeb9d244abd703b215.png

36927746ae2e44bca270c2a0a128cc7b.png

77a7b97c4a8f432dbff764eaaf4199d0.png

将发送端运行多次,模拟聊天室,都发送消息 ,

246a0abf76594d3da69e336edf254cba.png

UDP三种通信方式

e6c2101d26ac465799f2b54595e82b39.png

6fe7faa569df4a95919cfe51cebb4cdf.png

060021c1d4134dc08138d8c2c4ff909b.png

创建多个接收端: 

0c829c2a67a0442a8aaa9781e03e185c.png

d571ae5e5f2f4369b9279a4a593cb334.png

396e3764234e4fb286e65dda5d3e376e.png

TCP通信程序 

0edc31a0c8834e769251ea196de0fbbf.png

0ff2eaf0d156440eaf85664d8d4467f6.png

da4db76fe3e04b10a95f9a7b4978f835.png

de87014d7c6b4a348fe1aaeeb0b74b7f.png

39cb20d7b6764b738e7ea32d01cccc2e.png

2fafb9b7f6e94b22a06f336bbb0eb9a9.png

419cfd55b6b149ca87a4275d648b4ff6.png

综合练习

1.多发多收

6e63c18cd9a94030a03adeae7348bd16.png

客户端: 

package com.company.net;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class TestOneClient {
    public static void main(String[] args) throws IOException {
        //1.创建Socket对象并连接服务端
        Socket socket = new Socket("127.0.0.1",10000);
        //2.写出数据
        Scanner sc = new Scanner(System.in);
        OutputStream os = socket.getOutputStream();
        while (true) {

            System.out.println("请输入您想发送的信息");
            String str = sc.nextLine();
            if (str.equals("886")){
                break;
            }
            os.write(str.getBytes());

        }
        //3.释放资源
        socket.close();
    }
}

 服务端:

package com.company.net;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class TestOneServer {
    public static void main(String[] args) throws IOException {
        //1.创建对象绑定10000端口
        ServerSocket ss = new ServerSocket(10000);
        //2.等待客户端来连接
        Socket socket = ss.accept();
        //3.读取数据
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        int b;
        while ((b = isr.read()) != -1){
            System.out.print((char)b);
        }
        //4.释放资源
        socket.close();
        ss.close();
    }
}

8ae29882de3d4303b974b8f52b010bba.png

c609eb5f4673403ab45378e456a38ec7.png

2.接收并反馈

0deeb339016843f4b6740f55ef636d03.png

客户端:

package com.company.net;

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

public class TestTwoCilent {
    public static void main(String[] args) throws IOException {
        //1.创建Socket对象并连接服务端
        Socket socket = new Socket("127.0.0.1",10000);
        //2.写出数据
        String str = "见到你很高兴";
        OutputStream os = socket.getOutputStream();
        os.write(str.getBytes());

        //结束输出标志,没有这个,server就会一直在while里面,不会进行回写
        socket.shutdownOutput();
        //接收server发的消息
        InputStream is = socket.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        int b;
        while ((b = isr.read()) != -1){
            System.out.println((char)b);
        }
        socket.close();
    }
}

 服务端:

package com.company.net;

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

public class TestTwoServer {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10000);
        Socket socket = ss.accept();
        InputStream is = socket.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        int b;
        while ((b = isr.read()) != -1){
            System.out.print((char)b);
        }
        //回写数据
        String str = "到底有多开心啊?";
        OutputStream os = socket.getOutputStream();
        os.write(str.getBytes());


        socket.close();
        ss.close();
        }


    }

7dfdd6e5f05f432f9dc0665b278c468f.png

3.上传文件

b894407be6a84d36b3d358cf1da9667c.png

 服务端:

package com.company.net;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Test3Server {
    public static void main(String[] args) throws IOException {
        //1.创建对象并绑定端口
        ServerSocket ss = new ServerSocket(10000);
        //2.等待客户端来连接
        Socket socket = ss.accept();
        //3.读取数据并把保存到本地文件中
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\com\\company\\net\\serverdir\\a.jpg"));
        int len;
        byte[] bytes = new byte[1024];
        while ((len = bis.read(bytes)) != -1){
            bos.write(bytes,0,len);
        }
        //回写数据
        BufferedWriter bw = new BufferedWriter((new OutputStreamWriter(socket.getOutputStream())));
        bw.write("上传成功");
        bw.newLine();
        bw.flush();

        socket.close();
        ss.close();
    }
}

客户端: 

package com.company.net;

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

public class Test3Client {
    public static void main(String[] args) throws IOException {
        //1.创建Socket对象,并连接服务器
        Socket socket = new Socket("127.0.0.1",10000);
        //2.读取本地文件中的数据,并写到服务器当中
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\com\\company\\net\\clientdir\\000001.jpeg"));
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1){
            bos.write(bytes,0,len);
        }
        //往服务器写出结束标记
        socket.shutdownOutput();
        //接收服务器的回写数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String s = br.readLine();
        System.out.println(s);

        socket.close();
    }


}

0f1b3ac3add845f999bfe546eedb5d93.png

4.文件名重复问题 

26ace7a56ab14456b6527f5be2274b7a.png

06117b9b81fb4c9aa981a266193fdd2e.png

package com.company.net;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

public class Test3Server {
    public static void main(String[] args) throws IOException {
        //1.创建对象并绑定端口
        ServerSocket ss = new ServerSocket(10000);
        //2.等待客户端来连接
        Socket socket = ss.accept();
        //3.读取数据并把保存到本地文件中
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        String name = UUID.randomUUID().toString().replace("-","");
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\com\\company\\net\\serverdir\\"+name+".jpg"));
        int len;
        byte[] bytes = new byte[1024];
        while ((len = bis.read(bytes)) != -1){
            bos.write(bytes,0,len);
        }
        //回写数据
        BufferedWriter bw = new BufferedWriter((new OutputStreamWriter(socket.getOutputStream())));
        bw.write("上传成功");
        bw.newLine();
        bw.flush();

        socket.close();
        ss.close();
    }
}

5.上传文件(多线程版)

0bb6fdc42f01488996ef12954ca4e7a5.png

6e4c19193f2b46b1b85c0736357c4fa5.png

 服务端:

package com.company.net;

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

public class Test3Server {
    public static void main(String[] args) throws IOException {
        //1.创建对象并绑定端口
        ServerSocket ss = new ServerSocket(10000);
        while (true) {
            //2.等待客户端来连接
            //开启一条线程
            //一个用户就对应服务端的一条线程
            Socket socket = ss.accept();
            new Thread(new MyRunnable(socket)).start();
        }
    }
}

 线程:

package com.company.net;

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

public class MyRunnable implements Runnable{
    Socket socket;
    public MyRunnable(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            //3.读取数据并把保存到本地文件中
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
            String name = UUID.randomUUID().toString().replace("-","");
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\com\\company\\net\\serverdir\\"+name+".jpg"));
            int len;
            byte[] bytes = new byte[1024];
            while ((len = bis.read(bytes)) != -1){
                bos.write(bytes,0,len);
            }
            //回写数据
            BufferedWriter bw = new BufferedWriter((new OutputStreamWriter(socket.getOutputStream())));
            bw.write("上传成功");
            bw.newLine();
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

6.上传文件(线程池优化) 

3f906e0c7ea440149768d0fc8c5fefb4.png

package com.company.net;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Test3Server {
    public static void main(String[] args) throws IOException {
        //创建线程池
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3,//核心线程数量
                16,//线程池总大小
                60,//空闲时间
                TimeUnit.SECONDS,//空闲时间单位
                new ArrayBlockingQueue<>(2),//队列
                Executors.defaultThreadFactory(),//线程工厂,让线程池如何创建线程对象
                new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略
        );
        //1.创建对象并绑定端口
        ServerSocket ss = new ServerSocket(10000);
        while (true) {
            //2.等待客户端来连接
            //开启一条线程
            //一个用户就对应服务端的一条线程
            Socket socket = ss.accept();
//            new Thread(new MyRunnable(socket)).start();
            pool.submit(new MyRunnable(socket));
        }
    }
}

7.接收浏览器消息并打印 

3f80f7dad4b644dea54fce1eb27e4b62.png

858f8afd06ac414c9337a7af2007ecbc.png

 8.控制台版聊天室

acbd22636a294aa880b4e6288ccfd7d4.png

项目名称

​ 利用TCP协议,做一个带有登录,注册的无界面,控制台版的多人聊天室。

使用到的知识点

​ 循环,判断,集合,IO,多线程,网络编程

准备工作

在当前模块下新建txt文件,文件中保存正确的用户名和密码

文件内容如下:

//左边是用户名
//右边是密码
zhangsan=123
lisi=1234
wangwu=12345

需求描述

① 客户端启动之后,需要连接服务端,并出现以下提示:

服务器已经连接成功
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:

②选择登录之后,出现以下提示:

服务器已经连接成功
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:
1
请输入用户名

 ③需要输入用户名和密码,输入完毕,没有按回车时,效果如下:

服务器已经连接成功
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:
1
请输入用户名
zhangsan
请输入密码
123

④按下回车,提交给服务器验证

服务器会结合txt文件中的用户名和密码进行判断

根据不同情况,服务器回写三种判断提示:

服务器回写第一种提示:登录成功
服务器回写第二种提示:密码有误  
服务器回写第三种提示:用户名不存在

⑤客户端接收服务端回写的数据,根据三种情况进行不同的处理方案

登录成功的情况, 可以开始聊天,出现以下提示:

服务器已经连接成功
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:
1
请输入用户名
zhangsan
请输入密码
123
1
登录成功,开始聊天
请输入您要说的话

 密码错误的情况,需要重新输入,出现以下提示:

服务器已经连接成功
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:
1
请输入用户名
zhangsan
请输入密码
aaa
密码输入错误
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:

 用户名不存在的情况,需要重新输入,出现以下提示:

服务器已经连接成功
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:
1
请输入用户名
zhaoliu
请输入密码
123456
用户名不存在
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:

⑥如果成功登录,就可以开始聊天,此时的聊天是群聊,一个人发消息给服务端,服务端接收到之后需要群发给所有人

提示:

​ 此时不能用广播地址,因为广播是UDP独有的

​ 服务端可以将所有用户的Socket对象存储到一个集合中

​ 当需要群发消息时,可以遍历集合发给所有的用户

​ 此时的服务端,相当于做了一个消息的转发

转发核心思想如下图所示:

4565653a9e2e4129b017264eab1f1243.png

其他要求:
用户名和密码要求:

要求1:用户名要唯一,长度:6~18位,纯字母,不能有数字或其他符号。

要求2:密码长度3~8位。第一位必须是小写或者大写的字母,后面必须是纯数字。

客户端:

拥有登录、注册、聊天功能。

① 当客户端启动之后,要求让用户选择是登录操作还是注册操作,需要循环。

如果是登录操作,就输入用户名和密码,以下面的格式发送给服务端

username=zhangsan&password=123

如果是注册操作,就输入用户名和密码,以下面的格式发送给服务端

username=zhangsan&password=123

② 登录成功之后,直接开始聊天。

服务端:

① 先读取本地文件中所有的正确用户信息。

② 当有客户端来链接的时候,就开启一条线程。

③ 在线程里面判断当前用户是登录操作还是注册操作。

④ 登录,校验用户名和密码是否正确

⑤ 注册,校验用户名是否唯一,校验用户名和密码的格式是否正确

⑥ 如果登录成功,开始聊天

⑦ 如果注册成功,将用户信息写到本地,开始聊天


客户端:

package com.company.net.chatRoom.Client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class ClientRunnable implements Runnable {
    //创建Socket对象,用于接收当前客户端的连接对象
    Socket socket;

    //有参构造方法:用于创建一个单独线程来专门接收服务端转发过来的聊天记录
    public ClientRunnable(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String msg = br.readLine();
            System.out.println(msg);
        } catch (IOException e) {
            System.out.println("群聊已解散");
//            e.printStackTrace();
            System.exit(0);
        }
    }
}
package com.company.net.chatRoom.Client;
import java.io.*;
import java.net.Socket;
import java.util.Properties;
import java.util.Scanner;

public class Client {
    static  Scanner sc = new Scanner(System.in);
    public static void main(String[] args) throws IOException {
        //连接服务器
        Socket socket = new Socket("127.0.0.1",10000);
        System.out.println("服务器已经连接成功");

        //主界面
        while(true){
            System.out.println("==============欢迎来到奥利给聊天室================");
            System.out.println("1.登录");
            System.out.println("2.注册");
            System.out.println("请输入您的选择:");
            String choose = sc.nextLine();

            switch (choose){
                case "1":
                    //登录
                    login(socket);
                    break;
                case "2":
                    //注册
                    register(socket);
                    break;
                case "3":
                    System.out.println("目前还没此选项~请重试");
                    break;
            }
        }
    }
    //用户注册
    private static void register(Socket socket) throws IOException {
        //获取输出流:用于向服务端写出数据
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        //读取本地文件中所有正确用户信息(用户名和密码)
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("E:\\Defect_Detection\\Code\\JAVA\\my\\src\\com\\company\\net\\chatRoom\\ServerDir\\userInfro");
        prop.load(fis);
        fis.close();

        //键盘录入用户名和密码
        System.out.println("请输入注册的用户名:");
        String username = sc.nextLine();
        //要求1.用户名唯一
        if(prop.containsKey(username)){
            System.out.println("用户名已存在!");
            return;
        }
        // 长度:6~18位,纯字母,不能有数字或其他符号。
        if (!username.matches("[a-zA-Z]{6,18}")) {
            System.out.println("注册的用户名不符合要求(长度6~18位、纯字母、不能有数字或其他符号)");
            return;
        }

        System.out.println("请输入注册的密码:");
        String password = sc.nextLine();
        System.out.println("请再次确认注册密码:");
        String okPassword = sc.nextLine();
        // 要求2:密码长度3~8位。第一位必须是小写或者大写的字母,后面必须是纯数字。
        String passwordRegex = "[a-zA-Z]{1}[0-9]{2,7}";
        if (!(password.matches(passwordRegex) || okPassword.matches(passwordRegex))){
            System.out.println("注册的密码不符合要求(长度3~8位,第一位必须是小写或者大写的字母,后面必须是纯数字)");
            return;
        }

        //判断两次密码不一致
        if (!password.equals(okPassword)) {
            System.out.println("您输入的两次密码不一致!!");
            return;
        }

        //拼接用户名和密码的格式为:username=password
        StringBuilder sb = new StringBuilder();
        sb.append("username=").append(username).append("&password=").append(password);

        //第一次写的是执行注册操作
        bw.write("register");
        bw.newLine(); //换行
        bw.flush(); //刷新

        //第二次写的是用户名、密码的信息
        //往服务端写出用户名和密码
        bw.write(sb.toString());
        bw.newLine();
        bw.flush();

        //接收服务端回写的数据
        //获取输入流
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String message = br.readLine();
        System.out.println("服务端回馈数据:" + message);
        // 1: 注册成功
        if (message.equals("1")) {
            System.out.println("注册成功~");
            //开启一条单独的线程,专门用来接收服务端发送过来的聊天记录
            new Thread(new ClientRunnable(socket)).start();
            //开始聊天:就是把消息写到服务端,然后服务端会将当前客户端的消息群发给所有在线的客户端
            talkServer(bw);
        }
    }
    //用户登录
    private static void login(Socket socket) throws IOException {
        //获取输出流:用于向服务端写出数据
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

        //键盘录入用户名和密码
        System.out.println("请输入用户名:");
        String username = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();
        //拼接用户名和密码
        StringBuilder sb = new StringBuilder();
        sb.append("username=").append(username).append("&password=").append(password);

        //第一次写的是执行登录操作
        bw.write("login");
        bw.newLine();//换行
        bw.flush();

        //第二次写的是用户名、密码的信息
        //往服务端写出用户名和密码
        bw.write(sb.toString());
        bw.newLine();
        bw.flush();

        //接收服务端回写的数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String message = br.readLine();
        System.out.println("服务器回馈数据:"+message);

        // 1: 登录成功;2: 密码有误;3: 用户名不存在
        if (message.equals("1")){
            System.out.println("登陆成功");
            //开启一条单独的线程,专门用来接收服务端发送过来的聊天记录
            new Thread(new ClientRunnable(socket)).start();
            //开始聊天:就是把消息写到服务端,然后服务端会将当前客户端的消息群发给所有在线的客户端
            talkServer(bw);
        }else if (message.equals("2")){
            System.out.println("密码有误!!");
        }else if (message.equals("3")){
            System.out.println("用户名不存在");
        }
    }

    private static void talkServer(BufferedWriter bw) throws IOException {
        System.out.println("请输入您想说的话:");
        String message = sc.nextLine();
        //把输入的话写给服务端
        bw.write(message);
        bw.newLine();
        bw.flush();
    }
}

服务端:

package com.company.net.chatRoom.Server;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Properties;

public class Server {
    //定义集合用于存储每一个客户端的连接对象
    public static ArrayList<Socket> socketList = new ArrayList<>();

    public static void main(String[] args) throws IOException {
        //创建ServerSocket对象,并绑定端口号
        ServerSocket ss = new ServerSocket(10000);
        //先读取本地文件中所有的正确用户信息(用户名和密码)
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("E:\\Defect_Detection\\Code\\JAVA\\my\\src\\com\\company\\net\\chatRoom\\ServerDir\\userInfro");
        prop.load(fis);
        fis.close();

        //当有客户端来连接的时候,就开启一条线程
        while (true) {
            Socket socket = ss.accept();
            System.out.println("有客户端来连接了~");
            new Thread(new ServerRunnable(socket, prop)).start();
        }
    }
}
package com.company.net.chatRoom.Server;

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

public class ServerRunnable implements Runnable{
    //创建Socket、Properties对象
    //socket: 用于接收每一个客户端的socket连接对象
    //prop: 用于接收所有的正确用户信息
    public static Properties prop;
    Socket socket;

    //线程类的有参构造方法:当有客户端来连接时,开启一条线程
    //并将该客户端的socket连接对象 和 所有的正确用户信息初始化给当前线程对象的socket、prop
    public ServerRunnable(Socket socket,Properties prop) {
        this.prop = prop;
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            //获取输入流:用于读取客户端的数据
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //读取用户名的选项
            while (true) {
                String choose = br.readLine();
                //判断当前用户是选择了登录操作还是注册操作
                switch (choose) {
                    case "login": //用户选择了登录
                        login(br);
                        break;
                    case "register": //用户选择了注册
                        register(br);
                        break;
                }
            }
        } catch (IOException e) {
//            throw new RuntimeException(e);
            System.out.println("有客户端下线了~");
        }
    }

    private void register(BufferedReader br) throws IOException {
        System.out.println("用户选择了注册~");
        //读取该用户输入的用户名和密码信息:username=xxx&password=xxx
        String userInfo = br.readLine();

        //根据 "&" 将用户名和密码进行分裂
        //分裂后得到一个字符串数组:
        // 0索引就是:username=xxx
        // 1索引就是:password=xxx
        String[] userInfoArr = userInfo.split("&");

        // 得到用户输入的用户名
        String usernameInput = userInfoArr[0].split("=")[1];
        System.out.println("用户输入的用户名为:" + usernameInput);

        // 得到用户输入的密码
        String passwordInput = userInfoArr[1].split("=")[1];
        System.out.println("用户输入的密码为:" + passwordInput);

        //拼接用户信息的格式为:username=password
        StringBuilder sb = new StringBuilder();
        sb.append(usernameInput).append("=").append(passwordInput);

        //将新注册的用户名和密码写入userInfo.txt文件中
        BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\Defect_Detection\\Code\\JAVA\\my\\src\\com\\company\\net\\chatRoom\\ServerDir\\userInfro", true));
        bw.write(sb.toString());
        bw.newLine();
        bw.flush();

        //新用户注册成功,回写一个1给客户端
        writerMessageClient("1");
        //把当前注册成功的客户端的socket连接对象存储到socketList集合中
        Server.socketList.add(socket);

        //接收客户端发过来的消息,群发给每一个客户端
        talkAllClient(br, usernameInput);
    }

    private void login(BufferedReader br) throws IOException {
        System.out.println("用户选择了登录~");
        //读取该用户输入的用户名和密码信息:username=xxx&password=xxx
        String userInfo = br.readLine();

        //根据 "&" 将用户名和密码进行分裂
        //分裂后得到一个字符串数组:
        // 0索引就是:username=xxx
        // 1索引就是:password=xxx
        String[] userInfoArr = userInfo.split("&");

        //再根据 "=" 将0索引的用户名
        //分裂后得到一个字符串数组
        // 0索引就是:username
        // 1索引就是:xxx
        String[] usernameArr = userInfoArr[0].split("=");
        // 得到用户输入的用户名
        String usernameInput = usernameArr[1];
        System.out.println("用户输入的用户名为:" + usernameInput);

        //再根据 "=" 将1索引的密码
        //分裂后得到一个字符串数组
        // 0索引就是:password
        // 1索引就是:xxx
        String[] passwordArr = userInfoArr[1].split("=");
        // 得到用户输入的密码
        String passwordInput = passwordArr[1];
        System.out.println("用户输入的密码为:" + passwordInput);


        //校验用户名是否正确(其实就是判断用户名是否存在)
        if (prop.containsKey(usernameInput)) {
            //用户名存在,则校验密码是否正确
            // 根据用户名获取对应的密码,并转换成字符串类型(方便比较)
            String rightPassword = prop.get(usernameInput) + "";
            if (rightPassword.equals(passwordInput)) {
                //程序到这里,说明用户输入用户名和密码都正确了,回写一个1(登录成功)给客户端
                writerMessageClient("1");
                //把当前登录成功的客户端的socket连接对象存储到socketList集合中
                Server.socketList.add(socket);
                //接收客户端发过来的消息,群发给每一个客户端
                talkAllClient(br, usernameInput);

            }else {
                //密码错误,回写一个2给客户端
                writerMessageClient("2");
            }
        }else {
            //用户名不存在,回写一个3给客户端
            writerMessageClient("3");
        }
    }

    private void talkAllClient(BufferedReader br, String username) throws IOException {
        while (true) {
            String message = br.readLine();
            System.out.println(username + "说:" + message);

            //将当前客户端发送过来的消息群发给所有在线的客户端
            for (Socket s : Server.socketList) {
                //s表示每一个客户端的连接对象
                writerMessageClient(s, username + "说:" + message);
            }
        }
    }
    //回写数据给客户端
    public void writerMessageClient(String message) throws IOException {
        //获取输出流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write(message);
        bw.newLine();//换行
        bw.flush();//刷新
    }

    //回写数据给客户端
    public void writerMessageClient(Socket socket, String message) throws IOException {
        //获取输出流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write(message);
        bw.newLine();//换行
        bw.flush();//刷新
    }
}

运行效果

先运行server,再运行client,按照命令走就行了

8eabb5e73ab848109402b70edc005307.png

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值