欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java客户端与服务端建立连接 线程 ServerSocket

程序员文章站 2024-03-23 10:18:04
...
package chat;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/*
 * 聊天客户端
 */
public class Client {
    /*
     * java.net.Scoket 套接字
     * 封装了TCP通讯,使用它可以通过网络连接到服务端并进行数据交换(通讯)
     */
    private Socket socket;
    /*
     * 构造方法
     * 用于初始化客户端
     */
    public Client() throws Exception{
        try {
            /*
             * 实例化Scoket的过程就是与远端计算机建立连接的过程
             * 实际上建立连接是客户端应用程序与服务端应用程序建立的连接
             * 那么这里实例化Scoket需要传入两个参数
             * 1:服务器端计算机的IP地址(用于在网络中找到服务器端电脑)
             * 2:端口号(通过端口号可以找到运行在服务器中的服务端应用程序)
             */
            System.out.println("正在连接服务器。。。");
            socket = new Socket("localhost",8080);
            System.out.println("已建立连接!");
        } catch (Exception e) {
            throw e;
        }
    }
    /*
     * 客户端的启动方法
     */
    public void start(){
        try {
            /*
             * Socket 提供方法:
             * OutputStream getOutputStream
             * 该方法可以获取一个字节输出流,通过该流写出的数据都会发送给远端计算机
             * 对于客户而言,远端就是服务端
             */

            Scanner scan = new Scanner(System.in);
            //客户端输出
            OutputStream out = socket.getOutputStream();
            //转换指定字符集
            OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
            //缓冲 字符输出流
            PrintWriter pw = new PrintWriter(osw,true);

            //启动读取服务端发送过来消息的线程
            ServerHandler handler = new ServerHandler();
            Thread t = new Thread(handler);
            t.start();


            System.out.println("开始聊天");
            String message = null;
            while(true){
                message = scan.nextLine();
                pw.println(message);
//              //读取服务端发送回来的内容
//              message = br.readLine() ;
//              System.out.println(message);
            }

        } catch (Exception e) {
            e.printStackTrace();
            }
    }
    /*
     * 该线程专门用来接收服务端发送过来的消息并输出到控制台
     */
    private class ServerHandler implements Runnable{
        public void run(){
            try {
                //客户端输入
                InputStream in  = socket.getInputStream();
                //字符
                InputStreamReader isr = new InputStreamReader(in,"utf-8");
                //字符
                BufferedReader br = new BufferedReader(isr);
                String message = null;
                while((message = br.readLine())!= null){
                    System.out.println(message);
                }

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

    public static void main(String[] args) {
        try {
            Client client = new Client();
            client.start();

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

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/*
 * 服务器端
 * 
 */
public class Server {
    /*
     * java.net.ServerSocket; 运行在服务端的ServerScoket主要职责:
     * 1:向OS申请服务端口(客户端就是同过这个端口与服务端建立连接的) 
     * 2:监听服务端口并建立与客户端的连接
     */
    private ServerSocket server;
    //存放所有客户端的输出流,用于广播消息
    private List<PrintWriter> allOut;
    /*
     * 服务端构造方法,用来初始化服务端
     */
    public Server() throws Exception {
        try {
            /*
             * 初始化ServerSocket的同时向系统申请服务器端口, 若该端口已经被其他程序占用则会抛出异常
             */
            server = new ServerSocket(8080);
            allOut = new ArrayList<PrintWriter>();

        } catch (Exception e) {
            // 将来在这里可以记录日志
            // 异常若不应当在这里处理,继续抛出
            throw e;
        }

    }

    /*
     * 服务端开始工作的方法
     */
    public void start() {

        try {
            /*
             * ServerSocket提供方法: Socket accept()
             * 该方法是一个阻塞方法,作用是监听服务端口,等待客户端的连接一旦一个客户端请求连接
             * 那么accept方法会解除阻塞,并返回一个Socket实例,服务器就可以通过这个Socket 
             * 实例与客户端交互
             */
            while(true){
            System.out.println("等待一个客户端连接。。");
            Socket socket = server.accept();
            System.out.println("一个客户端连接了");

            //启动一个线程来负责与该客户端交互
            ClientHandler handler = new ClientHandler(socket);
            Thread t = new Thread(handler);
            t.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private class ClientHandler implements Runnable {
        /*
         * 当前线程就是通过这个Socket与指定客户端交互的
         * 
         */

        private Socket socket;

        //当前客户端的地址信息
        private String host;


        public ClientHandler(Socket socket) {
            this.socket = socket;
            //获取该客户端地址信息(远端计算机地址信息)
            InetAddress address = socket.getInetAddress();
            host = address.getHostAddress();
        }

        public void run() {
            PrintWriter pw = null;
            try {
                /*
                 * Socket 提供方法: InputStream getInputStream()
                 * 通过这个流读取到的数据就是远端发送过来的数据
                 */
                // 服务端输入
                // 字节
                InputStream in = socket.getInputStream();
                // 字符
                InputStreamReader isr = new InputStreamReader(in, "utf-8");
                // 字符
                BufferedReader br = new BufferedReader(isr);

                // 服务端输出
                OutputStream out = socket.getOutputStream();
                // 转换指定字符集
                OutputStreamWriter osw = new OutputStreamWriter(out, "UTF-8");
                // 缓冲 字符输出流
                pw = new PrintWriter(osw, true);


                //将该客户端的输出流存入共享集合
                synchronized(allOut){//锁集合 保证多个线程不能同时调用
                    allOut.add(pw);
                }
                //广播当前客户端上线
                sendMessage(host+"上线了");

                String message = null;
                while ((message = br.readLine()) != null) {
                    Thread t = Thread.currentThread();
                    System.out.println("客户" +t.getName()+":"+ message);
//                  // 发送回给客户端
//                  pw.println("客户端说:" + message);

                    //遍历集合,发送给所有客户端
                    sendMessage(host+":"+message);

                }

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


                //处理客户端断开连接后的操作

                //将该客户端的输出流从共享集合中删除
                synchronized(allOut){
                    allOut.remove(pw);

                }
                sendMessage(host+"下线了");

                try{
                    /*
                     * 网络通讯完毕后关闭socket
                     */
                socket.close();
                }catch(Exception e){
                }

            }

        }
    /*
     * 将消息广播给所有客户端
     */
        private void sendMessage(String message){
            synchronized(allOut){
                for(PrintWriter o : allOut){
                    o.println(message);
                }
            }
        }
    }


    public static void main(String[] args) {
        try {
            Server server = new Server();
            server.start();

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

}
相关标签: java