java.net.Socketjava.net.ServerSocket实现简单的聊天程序

思路是这样的:
假设用户A与用户B进行聊天,用户B端使用ServerSocket作为服务端,而用户A使用Socket与用户B进行通信。

  • 这两个小程序需要运行在同一台电脑上,才能正常通信。
  • 运行时一定要先运行Server.class再运行Client.class
  • 通过异常来控制程序逻辑不是首选方式,但想了很久也没想出其它方法 >.<
用户A的代码如下(Client.java
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by DuanJiaNing on 2017/3/16.
 */
public class Client {

    public static void main(String[] args) {
        try {
            //创建Socket连接到本机的2003端口
            Socket socket = new Socket(InetAddress.getLocalHost(), 2003);
            println("**********您正在和:" + InetAddress.getLocalHost().getHostAddress() + " 聊天***********\n" + "输入信息,按回车即可发送!输入quit可结束会话并关闭程序。");
            OutputStream out = socket.getOutputStream();
            out.write("Hello 在么?".getBytes());

            //创建新线程负责打印接收到的消息
            new Thread(new ClientThread(socket.getInputStream())).start();

            //接收控制台输入
            BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
            //读取控制台输入,有输入且不为quit时进行发送
            String line;
            while ((line = bufr.readLine()) != null && !line.equals("quit")) {
                out.write(line.getBytes());
            }
            //关闭套接字
            //程序结束 这将导致ClientThread抛java.net.SocketException: Socket closed异常而结束(暂且就这样关闭这个线程)
            socket.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class ClientThread implements Runnable {
        InputStream input;

        public ClientThread(InputStream in) {
            this.input = in;
        }

        public void run() {
            byte[] bytes = new byte[1024];
            int len;
            try {
                //当收到消息时把消息打印到控制台
                while ((len = input.read(bytes)) != -1) {
                    println(new String(bytes, 0, len));
                }
            } catch (Exception e) {
                //当服务端或客户端结束会话时(输入quit时)会关闭socket从而抛出java.net.SocketException: Socket closed异常
                //隐藏此异常不做处理让程序继续“正常”运行(正常结束)
                if (!(e instanceof SocketException))
                    e.printStackTrace();
            }
            println("**********此次会话结束**********");
            System.exit(0);
        }
    }

    private static void println(String msg) {
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ");
        System.out.println(format.format(date) + msg);
    }

}

用户B代码(服务端)(Server.java
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by DuanJiaNing on 2017/3/16.
 */
public class Server {

    private static BufferedReader reader;

    public static void main(String[] args) {

        //监听本机(服务器)的2003端口
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(2003);
        } catch (IOException e) {
            e.printStackTrace();
        }
        while (true) {

                try {
                    println("************当前没有正在进行的会话***********");

                    //accept为阻塞时方法,当有连接时会返回连接着的socket对象
                    Socket socket = serverSocket.accept();
                    println("当前聊天对象来自:" + socket.getLocalAddress().getHostAddress() + "\n" + "输入信息,按回车即可发送!输入quit可结束此次会话。");

                    //创建新线程负责打印接收到的消息
                    InputStream in = socket.getInputStream();
                    new Thread(new ServerThread(in)).start();

                    //接收输入并发送给连接者
                    reader = new BufferedReader(new InputStreamReader(System.in));
                    String line;
                    OutputStream out = socket.getOutputStream();
                    while ((line = reader.readLine()) != null && !line.equals("quit")) {
                        out.write(line.getBytes());
                    }
                    //服务端主动结束会话
                    out.write("先不聊了。bye~".getBytes());

                    //关闭套接字
                    //程序结束 这将导致ServerThread抛java.net.SocketException: Socket closed异常而结束(暂且就这样关闭此次会话对应的线程)
                    socket.close();

                } catch (Exception e) {
                    //当客户端主动结束会话(输入quit时),而服务端试图发送信息时会抛出java.net.SocketException: Software caused connection abort: socket write error
                    //借这个异常让服务端回到等待客户端接入状态
                    if (e instanceof SocketException) {
                        println("您的聊天对象已离线\n");
                        continue;
                    }
                    else
                        e.printStackTrace();
                }
            }
    }

    static class ServerThread implements Runnable {
        InputStream input;

        public ServerThread(InputStream in) {
            this.input = in;
        }

        public void run() {
            byte[] bytes = new byte[1024];
            int len;
            try {
                //当收到消息时把消息打印到控制台
                while ((len = input.read(bytes)) != -1) {
                    println(new String(bytes, 0, len));
                }
            } catch (Exception e) {
                //当服务端或(输入quit时)会关闭socket从而抛出java.net.SocketException: Socket closed异常
                //当服务端输入quit时隐藏此异常不做处理让程序继续运行(回到等待客户端接入的状态)
                if (!(e instanceof SocketException)) {
                    e.printStackTrace();
                }
            }

        }
    }

    private static void println(String msg) {
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ");
        System.out.println(format.format(date) + msg);
    }
}

编译运行
  • 分别编译Server.java,Client.java文件
javac Server.java
javac Client.java
  • 先后运行Server.class和Client.class
java Server
java Client
  • 此时可看到:

java 类似odoo_通信

  • 接下来进行简单对话后在服务端输入quit可看到:

java 类似odoo_通信_02

  • 此时客户端已经关闭,而服务端会循环阻塞在accept方法上。此时只需再次运行Client.class(在dos终端切换到Client.class文件所在路径后输入java Client)就能再次连接到服务端
  • 服务端主动结束会话,此时时这样的:

java 类似odoo_java_03

有哪里可以改进的可以留言告诉我哦