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

posted on 2015-06-16 11:18  静以修身!  阅读(2079)  评论(0编辑  收藏  举报

导航