Java学习笔记之--------网络编程之Socket通信----聊天室实现

Socket通信

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。基于TCP/IP协议,建立稳定的点对点的通信。

特点:实时、快速、安全性高、占用系统资源多、效率低。

通常也称作"套接字",套接字是一种进程间的数据交换机制。这些进程既可以在同一机器上,也可以在通过网络连接的不同机器上。换句话说,套接字起到通信端点的作用。单个套接字是一个端点,而一对套接字则构成一个双向通信信道,使非关联进程可以在本地或通过网络进行数据交换。一旦建立套接字连接,数据即可在相同或不同的系统中双向或单向发送,直到其中一个端点关闭连接。

“请求----响应”模式

客户端:在网络通讯中,第一次主动发起通讯的程序被称作客户端(client)程序。

服务器:第一次通讯中等待连接的程序被称作服务器端(server)程序。

这里模拟了一个聊天室的简单的聊天功能:

首先我们先准备一个关闭流的工具类CloseUtil,以后流的关闭都可以通过调用这个方法来实现:

public class CloseUtil {
    public static void closeAll(Closeable... io){
        for (Closeable temp : io) {
            try {
                if (null != temp){
                    temp.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

然后我们实现数据的接收和发送:

public class Send implements Runnable{
    //控制台输入流
    private BufferedReader console;
    //管道输出流
    private DataOutputStream dos;
    //控制线程标识
    private boolean isRunning = true;
    //名称
    private String name;

    public Send() {
        console = new BufferedReader(new InputStreamReader(System.in));
    }
    public Send(Socket client, String name){
        this();
        try {
            dos = new DataOutputStream(client.getOutputStream());
            this.name = name;
            send(this.name);
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.closeAll(dos,console);
        }
    }

    /**
     * 1.从控制台接收数据
     * @return
     */
    private String getMsgFromConsole(){
        try {
            return console.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 2.发送数据
     */
    public void send(String msg){
        try {
            if (null != msg && !msg.equals("")){
                dos.writeUTF(msg);
                dos.flush();//强制刷新
            }
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.closeAll(dos,console);
        }
    }

    @Override
    public void run() {
        while (isRunning){
            send(getMsgFromConsole());
        }
    }
}
public class Receive implements Runnable{
    //输入流
    private DataInputStream dis;
    //线程标识
    private boolean isRunning = true;

    public Receive() {
    }

    public Receive(Socket client) {
        try {
            dis = new DataInputStream(client.getInputStream());
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.closeAll(dis);
        }
    }

    /**
     * 接收数据
     * @return
     */
    public String receive(){
        String msg = "";
        try {
            msg = dis.readUTF();
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.closeAll(dis);
        }
        return msg;
    }

    @Override
    public void run() {
        //线程体
        while (isRunning){
            System.out.println(receive());
        }
    }
}

最后是我们模拟客户端和服务器的实现:

public class Server {
    private List<MyChannel> all = new ArrayList<MyChannel>();

    public static void main(String[] args) throws IOException {
        new Server().start();
    }

    public void start() throws IOException {
        ServerSocket server = new ServerSocket(9999);
        while (true){
            Socket client = server.accept();
            MyChannel channel = new MyChannel(client);
            all.add(channel);//统一管理
            new Thread(channel).start();//一条道路
        }
    }

    /**
     * 一个客户端一条道路
     * 1.输入流
     * 2.输出流
     * 3.接收数据
     * 4.发送数据
     */
    class MyChannel implements Runnable{
        private DataInputStream dis;
        private DataOutputStream dos;
        private String name;
        //线程标识
        private boolean isRunning = true;

        public MyChannel(Socket client) {
            try {
                dis = new DataInputStream(client.getInputStream());
                dos = new DataOutputStream(client.getOutputStream());
                this.name = dis.readUTF();
                this.send("欢迎进入聊天室");
                this.sendOthers(this.name + "进入了聊天室", true);
            } catch (IOException e) {
                CloseUtil.closeAll(dis,dos);
                isRunning = false;
            }
        }

        /**
         * 读取数据
         * @return
         */
        private String receive(){
            String msg = "";
            try {
                msg = dis.readUTF();
            } catch (IOException e) {
                CloseUtil.closeAll(dis);
                isRunning = false;
                all.remove(this);//移除自身
            }
            return msg;
        }

        /**
         * 发送数据
         * @return
         */
        private void send(String msg){
            if (null==msg || msg.equals("")){
                return;
            }
            try {
                dos.writeUTF(msg);
                dos.flush();
            } catch (IOException e) {
                CloseUtil.closeAll(dos);
                isRunning = false;
                all.remove(this);//移除自身
            }
        }

        /**
         * 发送给其他的客户端
         */
        private void sendOthers(String msg, boolean sys){
            //是否为私聊
            if (msg.startsWith("@") && msg.indexOf(":")>-1){ //约定私聊以@开始
                //获取name
                String name = msg.substring(1, msg.indexOf(":"));
                String content = msg.substring(msg.indexOf(":") + 1);
                for (MyChannel other : all) {
                    if (other.name.equals(name)){
                        other.send(this.name + "对您悄悄的说:" + content);
                    }
                }
            } else {
                //遍历容器
                for (MyChannel other : all) {
                    if (other == this){
                        continue;
                    }
                    if (sys){
                        //系统消息
                        other.send("系统信息:" + msg);
                    } else {
                        //发送给其他客户端
                        other.send(this.name + "对所有人说:" + msg);
                    }
                }
            }

        }

        @Override
        public void run() {
            while (isRunning){
                sendOthers(receive(), false);
            }
        }
    }
}
public class Client {
    public static void main(String[] args) throws IOException {
        System.out.println("请输入名称:");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String name = br.readLine();
        if (name.equals("")){
            return;
        }
        Socket client = new Socket("localhost",9999);
        new Thread(new Send(client, name)).start();//一条路径
        new Thread(new Receive(client)).start();//一条路径
    }
}

下面是我们的运行结果,先启动Server。然后启动Client,为第一个进程命名为a;然后再启动一个进程,命名为b;最后再启动一个进程并命名为c。在c进程中输入“大家好”,则a进程和b进程都可以收到这条消息,在c进程中输入“@a:小a同学,你好。”,则我们只能在a进程中收到这条消息。并且在b和c启动的时候,会有系统消息提示,b或者c进入聊天室。截图如下,依次为abc三个进程的截图:

以上就是模拟简单的群聊功能的实现。还可以在结束进程的时候添加系统消息提示某进程离开聊天室,只要对以上代码稍作修改即可。这里没有实现。

以上为尚学堂Java300集教学视频中裴新老师所教授的网络编程相关课程的笔记。

猜你喜欢

转载自blog.csdn.net/wangruoao/article/details/84033381