模拟QQ聊天服务端与客户端互相沟通时while循环产生的异常---java.net.BindException: Address already in use: JVM_Bind

大家好,我是一位在java学习圈中不愿意透露姓名并苟且偷生的小学员,如果文章有错误之处,还望海涵,欢迎多多指正
如果你从本文学到有用的干货知识,那么请您尽量点赞,关注,评论,收藏

今天模拟QQ服务端与客户端聊天时想要实现一个多线程(即多个客户端)的效果,于是根据实际问题需要创建了HashMap集合来存储用户信息,在用while循环模拟持续添加的过程中出现了异常
先来看看服务端的代码如何实现:
package server;

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

public class Server {

    //为了找寻用户信息方便,采用HashMap进行存储,静态无需创建对象,类名点即可
    private static HashMap<String,User> userBox = new HashMap<>();

    public static void main(String[] args){

        try {
            while(true) {
                System.out.println("服务端启动");
                ServerSocket serverSocket = new ServerSocket(9999);
                Socket socket = serverSocket.accept();
                //每获取到一个用户信息就添加进集合中
                //通过返回的socket获取一个字节型输入流
                InputStream is = socket.getInputStream();
                //将输入流包装成低级的字符型输入流,因为高级流里面不能直接放下字节型输入流
                InputStreamReader isr = new InputStreamReader(is);
                //构建高级字符型输入流 因为用BufferedReader流中有readLine方法可以直接读取一行
                BufferedReader br = new BufferedReader(isr);
                String key = br.readLine();
                //接收客户端的传过来的uid 并存入集合中
                Server.userBox.put(key,new User(key,socket));
                System.out.println("连接成功");
                System.out.println(Server.userBox.get(key).getSocket());
            }
//            //回客户端消息
//            Scanner input = new Scanner(System.in);
//            System.out.println("跟客户端说点什么吧:");
//            String message = input.nextLine();
//            OutputStream os = socket.getOutputStream();
//            PrintWriter writer = new PrintWriter(os);
//            writer.println(message);
//            writer.flush();
//            //接收客户端的消息
//            InputStream is = socket.getInputStream();
//            InputStreamReader isr = new InputStreamReader(is);
//            BufferedReader br = new BufferedReader(isr);
//            String value = br.readLine();
//            System.out.println(value);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
以下是javaBean(也称杜曼实体,即用来存储数据,没有其他用途的一种类,可以增强可读性)的写法:
package server;

import java.net.Socket;

public class User {

    private String uid;
    private Socket socket;

    public User(String uid, Socket socket) {
        this.uid = uid;
        this.socket = socket;
    }

    public String getUid() {
        return uid;
    }
    public Socket getSocket() {
        return socket;
    }
}
客户端的具体实现:
package client;

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

public class Client {
    public static void main(String[] args){

        try {
            Scanner input = new Scanner(System.in);
            while(true) {
                System.out.println("客户端启动");
                Socket socket = new Socket("localhost", 9999);
                System.out.println("请输入自己的账号:");
                String uid = input.nextLine();
                //创建字节型输出流
                OutputStream os = socket.getOutputStream();
                //将os构建成PrintWriter 以便使用println方法写一行
                PrintWriter writer = new PrintWriter(os);
                //将客户端的uid传给服务端
                writer.println(uid);
                //将数据从流管道中推出
                writer.flush();
            }
//            //接收服务端的消息
//            InputStream is = socket.getInputStream();
//            InputStreamReader isr = new InputStreamReader(is);
//            BufferedReader br = new BufferedReader(isr);
//            String value = br.readLine();
//            System.out.println(value);
//            //回服务端消息
//            System.out.println("跟服务端说点什么吧:");
//            String message = input.nextLine();
//            OutputStream os = socket.getOutputStream();
//            PrintWriter writer = new PrintWriter(os);
//            writer.println(message);
//            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

接下来我们看看这个异常是什么:
服务端启动
连接成功
Socket[addr=/127.0.0.1,port=*****,localport=9999]
服务端启动
java.net.BindException: Address already in use: JVM_Bind
	at java.net.DualStackPlainSocketImpl.bind0(Native Method)
	at java.net.DualStackPlainSocketImpl.socketBind(DualStackPlainSocketImpl.java:106)
	at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:387)
	at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:190)
	at java.net.ServerSocket.bind(ServerSocket.java:375)
	at java.net.ServerSocket.<init>(ServerSocket.java:237)
	at java.net.ServerSocket.<init>(ServerSocket.java:128)
	at server.Server.main(Server.java:18)
刚刚出现这个异常的时候我也懵了,为什么呢?通过运行结果,于是我在服务端ServerSocket类型的对象的创建之后添加了一行输出System.out.println("--------------");再次运行发现这行没有输出,于是我知道是ServerSocket类型的对象的创建出了问题,想了一会我知道是因为同样的端口号是唯一的(即不能同时使用同样的端口号),比如9999这个端口号已经用来创建ServerSocket类型的对象,再用9999来创建就会出现这样的异常
那么如何解决这个问题呢,于是我采用了简单粗暴的方法修改了服务端和客户端的代码,具体代码如下:
package server;

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

public class Server {

    //为了找寻用户信息方便,采用HashMap进行存储,静态无需创建对象,类名点即可
    private static HashMap<String,User> userBox = new HashMap<>();

    public static void main(String[] args){

        try {
            for(int i = 0 ; i < 10 ; i++) {
                System.out.println("服务端启动");
                ServerSocket serverSocket = new ServerSocket(9990+i);
                Socket socket = serverSocket.accept();
                //每获取到一个用户信息就添加进集合中
                //通过返回的socket获取一个字节型输入流
                InputStream is = socket.getInputStream();
                //将输入流包装成低级的字符型输入流,因为高级流里面不能直接放下字节型输入流
                InputStreamReader isr = new InputStreamReader(is);
                //构建高级字符型输入流 因为用BufferedReader流中有readLine方法可以直接读取一行
                BufferedReader br = new BufferedReader(isr);
                String key = br.readLine();
                //接收客户端的传过来的uid 并存入集合中
                Server.userBox.put(key,new User(key,socket));
                System.out.println("连接成功");
                System.out.println(Server.userBox.get(key).getSocket());
            }
//            //回客户端消息
//            Scanner input = new Scanner(System.in);
//            System.out.println("跟客户端说点什么吧:");
//            String message = input.nextLine();
//            OutputStream os = socket.getOutputStream();
//            PrintWriter writer = new PrintWriter(os);
//            writer.println(message);
//            writer.flush();
//            //接收客户端的消息
//            InputStream is = socket.getInputStream();
//            InputStreamReader isr = new InputStreamReader(is);
//            BufferedReader br = new BufferedReader(isr);
//            String value = br.readLine();
//            System.out.println(value);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

package client;

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

public class Client {
    public static void main(String[] args){

        try {
            Scanner input = new Scanner(System.in);
            for(int i = 0 ; i < 10 ; i++) {
                System.out.println("客户端启动");
                Socket socket = new Socket("localhost", 9990+i);
                System.out.println("请输入自己的账号:");
                String uid = input.nextLine();
                //创建字节型输出流
                OutputStream os = socket.getOutputStream();
                //将os构建成PrintWriter 以便使用println方法写一行
                PrintWriter writer = new PrintWriter(os);
                //将客户端的uid传给服务端
                writer.println(uid);
                //将数据从流管道中推出
                writer.flush();
            }
//            //接收服务端的消息
//            InputStream is = socket.getInputStream();
//            InputStreamReader isr = new InputStreamReader(is);
//            BufferedReader br = new BufferedReader(isr);
//            String value = br.readLine();
//            System.out.println(value);
//            //回服务端消息
//            System.out.println("跟服务端说点什么吧:");
//            String message = input.nextLine();
//            OutputStream os = socket.getOutputStream();
//            PrintWriter writer = new PrintWriter(os);
//            writer.println(message);
//            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端运行结果如下:
服务端启动
连接成功
Socket[addr=/127.0.0.1,port=*****,localport=9990]
服务端启动
连接成功
Socket[addr=/127.0.0.1,port=*****,localport=9991]
服务端启动
连接成功
Socket[addr=/127.0.0.1,port=*****,localport=9992]
服务端启动
客户端运行结果如下:
客户端启动
请输入自己的账号:
123
客户端启动
请输入自己的账号:
456
客户端启动
请输入自己的账号:
789
客户端启动
请输入自己的账号:

如上结果只试了三组测试,于是实现了将用户信息存入服务端的效果,感兴趣的小伙伴可以试着加一个登入方法,而学过数据库的小伙伴就不用我这么low的方法啦

猜你喜欢

转载自blog.csdn.net/bw_cx_fd_sz/article/details/107449791