• Java多线程与网络编程综合使用


    处理多线程与网络编程最为经典的例子莫过于聊天室,那我就聊天室案例作为一个回顾。
    首先,我们来看以下代码:

    package MultiTCP;
    
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * 必须先启动再连接
     * 1、创建服务器 指定端口 ServerSocket(int port)
     * 2、接收客户端的连接  阻塞式
     * 3、发送数据+接收数据
     * 
     * 接收多个客户端
     */
    @SuppressWarnings("all")
    public class MultiServer {
        public static void main(String[] args) throws IOException {
            //1、创建服务器 指定端口
            ServerSocket server = new ServerSocket(8888);
            while(true)//死循环 一个accept 一个客户端
            {
                //2、接收客户端的连接
                Socket socket = server.accept();
                System.out.println("一个客户端建立连接");
                //2、发送数据
                String msg = "欢迎使用";
                //3、输出流
                /*BufferedWriter bw = new BufferedWriter(
                        new OutputStreamWriter(
                                socket.getOutputStream()));
                bw.write(msg);
                bw.newLine();//一定要加行结束符,不然读不到数据
                bw.flush();*/
                DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
                dos.writeUTF(msg);
                dos.flush();
            }
        }
    }
    package MultiTCP;
    
    import java.io.DataInputStream;
    import java.io.IOException;
    import java.net.Socket;
    import java.net.UnknownHostException;
    /**
     * 1、创建客户端 必须指定服务器+端口  此时就在连接
     * Socket(String host,int port)
     * 2、接收数据+发送数据
     */
    @SuppressWarnings("all")
    public class Client {
        public static void main(String[] args) throws UnknownHostException, IOException {
            //1、创建客户端 必须指定服务器+端口  此时就在连接
            Socket client = new Socket("localhost",8888);
            //2、接收数据
            /*BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
            String echo = br.readLine();//阻塞式方法
            System.out.println(echo);*/
            DataInputStream dis = new DataInputStream(client.getInputStream());
            String echo = dis.readUTF();
            System.out.println(echo);
        }
    }

    以上代码存在的问题
    服务器为一个客户端发送数据完毕才能连接下一个客户端。
    因此,为了解决上述的问题,我们需要为服务器端创建多线程操作。

    首先我们需要为聊天室添加发送数据和接收数据。

    package CSNet;
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * 创建服务器
     */
    @SuppressWarnings("all")
    public class Server {
        public static void main(String[] args) throws IOException {
            ServerSocket server = new ServerSocket(9999);
            Socket client = server.accept();
    
            //写出数据
            //输入流
            DataInputStream dis = new DataInputStream(client.getInputStream());
            String msg = dis.readUTF();
            System.out.println("服务器收到"+msg);
    
            //输出流
            DataOutputStream dos = new DataOutputStream(client.getOutputStream());
            dos.writeUTF("服务器发送给客户端"+msg);
            dos.flush();                
        }
    }
    
    package CSNet;
    
    import java.io.BufferedReader;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    /**
     * 创建客户端:发送数据+接收数据
     * 写出数据:输出流
     * 读取数据:输入流
     * 输入流与输出流在同一个线程内,因此我们应该让 输入流与输出流彼此独立
     */
    @SuppressWarnings("all")
    public class Client {
        public static void main(String[] args) throws UnknownHostException, IOException {
            Socket client = new Socket("localhost",9999);
            //控制台的输入流
            BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
            DataOutputStream dos = new DataOutputStream(client.getOutputStream());
            DataInputStream dis = new DataInputStream(client.getInputStream());
    
            while(true)
            {
                String info = console.readLine();
                //输出流
                dos.writeUTF(info);
                dos.flush();
    
                //输入流
                String msg = dis.readUTF();
                System.out.println(msg);
            }
    
        }
    }

    以上的代码存在输入流与输出流在同一个线程内问题,因此我们应该让输入流与输出流彼此独立。


    接下来我们是需要实现多线程,让输入流与输出流分离。对客户端实现多线程。

    客户端发送数据

    package ThreadNet;
    
    import java.io.BufferedReader;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.Socket;
    
    /**
     * 发送数据线程:用于发送数据
     */
    public class Send implements Runnable{
        //控制台的输入流
        private BufferedReader console;
        //管道的数据输出流
        private DataOutputStream dos;
        //控制线程标识
        private boolean isRunning = true;
    
        //初始化
        public Send() {
            console = new BufferedReader(new InputStreamReader(System.in)); 
        }
    
        public Send(Socket client)
        {
            this();
            try {
                dos = new DataOutputStream(client.getOutputStream());
            } catch (IOException e) {
            }
        }
    
        //1、从控制台接收数据
        private String getMsgFromConsole()
        {
            try {
                return console.readLine();
            } catch (IOException e) {
            }
            return "";
        }
    
        /**
         * 1、从控制台接收数据
         * 2、发送数据
         */
        public void send()
        {
            String msg = getMsgFromConsole();
            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();
            }
        }
    }
    

    客户端接收数据

    package ThreadNet;
    
    import java.io.DataInputStream;
    import java.io.IOException;
    import java.net.Socket;
    
    /**
     * 接收线程:用于接收数据
     */
    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);
            }
        }
    
        //接收数据的方法
        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());
            }   
        }
    }
    

    客户端

    package ThreadNet;
    
    import java.io.IOException;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    /**
     * 创建客户端:发送数据+接收数据
     * 写出数据:输出流
     * 读取数据:输入流
     * 输入流与输出流在同一个线程内 应该独立出来
     */
    public class Client {
        public static void main(String[] args) throws UnknownHostException, IOException {
            Socket client = new Socket("localhost",9999);
            new Thread(new Send(client)).start();//一条路径
            new Thread(new Receive(client)).start();//一条路径  
        }
    }

    服务器

    package ThreadNet;
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * 创建服务器
     */
    public class Server {
        public static void main(String[] args) throws IOException {
            ServerSocket server = new ServerSocket(9999);
            while(true){
                Socket client = server.accept();
                //写出数据
                //输入流
                DataInputStream dis = new DataInputStream(client.getInputStream());
                //输出流
                DataOutputStream dos = new DataOutputStream(client.getOutputStream());
                while(true)
                {       
                    String msg = dis.readUTF();
                    //System.out.println(msg);      
                    dos.writeUTF("服务器收到数据并返回"+msg);
                    dos.flush();
                }
            }
        }
    }

    关闭流

    package ThreadNet;
    
    import java.io.Closeable;
    import java.io.IOException;
    
    /**
     * 关闭流的方法
     */
    public class CloseUtil {
        public static void closeAll(Closeable ... io)
        {
            for(Closeable temp:io)
            {
                if(null==temp)
                {
                    try {
                        temp.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    这个方仍然存在问题,服务器端只能够一个一个的接收,必须要等到上一条执行完,才能进入下一条,存在所谓的先后顺序,并不具备多线程的功能。因此我们也需要对服务器进行多线程。


    服务器

    package MultiServer;
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 创建服务器
     */
    public class Server {
        private List<MyChannel> all = new ArrayList<>();
    
        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、发送数据
         * @author Administrator
         *
         */
        class MyChannel implements Runnable
        {
            private DataInputStream dis;
            private DataOutputStream dos;
            private boolean isRunning = true;
            private String name;
    
            public MyChannel() {    
            }
            //初始化
            public MyChannel(Socket client)
            {
                try {
                    dis = new DataInputStream(client.getInputStream());
                    dos = new DataOutputStream(client.getOutputStream());
    
                    this.name = dis.readUTF();
                    //System.out.println(this.name);
                    this.send("欢迎进入聊天室");
    
                    sendOthers(this.name+"进入了聊天室",true);            
                } catch (IOException e) {
                    CloseUtil.closeAll(dos,dis);
                    isRunning = false;
                }
            }
    
            //接收数据
            private String receive()
            {
                String msg = "";
                try {
                    msg = dis.readUTF();
                } catch (IOException e) {
                    CloseUtil.closeAll(dis);
                    isRunning = false;
                    all.remove(this);//移除自身
                }
                return msg;
    
            }
    
            //发送数据
            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 contant = msg.substring(msg.indexOf(":")+1);
                    for(MyChannel other:all)
                    {
                        if(other.name.equals(name))
                        {
                            other.send(this.name+"对你悄悄的说:"+contant);
                        }
                    }
                }
                else {
                    for(MyChannel other:all)
                    {
                        if(other ==this)
                        {
                            continue;
                        }
    
                        if(sys==true)//系统信息
                        {
                            other.send("系统信息:"+msg);
                        }
                        else {
                            //发送其它客户端
                            other.send(this.name+"对所有人说"+msg);
                        }
    
                    }
                }
                /*
                //遍历容器
                for(MyChannel others:all)
                {
                    if(others == this)
                    {
                        continue;
                    }
                    //发送给其他客户端
                    others.send(msg);
                }*/
    
            }
    
            @Override
            public void run() {
                while(isRunning)
                {
                    sendOthers(receive(),false);
                }
            }
        }
    
    }
    

    发送信息(供客服端使用)

    package MultiServer;
    
    import java.io.BufferedReader;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.Socket;
    
    /**
     * 发送数据线程:用于发送数据
     */
    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) {
                //e.printStackTrace();
    
            }
        }
    
        //1、从控制台接收数据
        private String getMsgFromConsole()
        {
            try {
                return console.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "";
        }
    
        /**
         * 1、从控制台接收数据
         * 2、发送数据
         */
        public void send(String msg)
        {
    
            try {
                if(null!=msg&& !msg.equals(""))
                {
                    dos.writeUTF(msg);
                    dos.flush();//强制刷新
                }
            } catch (IOException e) {
                //e.printStackTrace();
                 isRunning = false;
                 CloseUtil.closeAll(dos,console);
            }
        }
    
        @Override
        public void run() {
            while(isRunning)
            {
                send( getMsgFromConsole());
            }
        }
    }

    接收信息(供客服端使用)

    package MultiServer;
    
    import java.io.DataInputStream;
    import java.io.IOException;
    import java.net.Socket;
    
    /**
     * 接收线程:用于接收数据
     */
    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);
            }
        }
    
        //接收数据的方法
        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());
            }   
        }
    }
    

    客户端

    package MultiServer;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.Socket;
    
    
    /**
     * 创建客户端:发送数据+接收数据
     * 写出数据:输出流
     * 读取数据:输入流
     * 输入流与输出流在同一个线程内 应该独立出来
     * 加入名称
     */
    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();//一条路径
    
        }
    }

    关闭流

    package MultiServer;
    
    import java.io.Closeable;
    import java.io.IOException;
    
    /**
     * 关闭流的方法
     */
    public class CloseUtil {
        public static void closeAll(Closeable ... io)
        {
            for(Closeable temp:io)
            {
                if(null==temp)
                {
                    try {
                        temp.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    运行结果:
    Client1

    Client2

  • 相关阅读:
    IOS开发中实现UITableView按照首字母将集合进行检索分组
    IOS开发中设置导航栏主题
    IOS中使用.xib文件封装一个自定义View
    IOS中将字典转成模型对象
    WindowsPhone8中LongListSelector的扩展解决其不能绑定SelectdeItem的问题
    WindowsPhone8.1 开发-- 二维码扫描
    tomcat 开机自启
    redis ubuntu 开机自启
    webStorm 中使用 supervisor 调试
    ubuntu 14.04 tab失效问题
  • 原文地址:https://www.cnblogs.com/lengzhijun/p/4580271.html
Copyright © 2020-2023  润新知