CoreJava学习11——网络编程51CTO博客 - 凯时娱乐

CoreJava学习11——网络编程51CTO博客

2019-01-03 15:39:44 | 作者: 瑞渊 | 标签: 线程,客户端,衔接 | 浏览: 1786

Java网络编程

1、Socket编程

Socket(套接字):封装着如端口号,ip地址,核算机名等信息的类。经过Socket咱们能够和长途核算机通讯。

网络通讯模型

C/S Client/Server

客户端通航运转在用户的核算机上,客户端是一个特有的程序,完成特有的功用,衔接效劳器进行通讯,谁建议衔接谁是用户。

效劳器端通常是等候客户端衔接,供给功用效劳并与之通讯。

B/S

固定了客户端和通讯协议和C/S结构。


通讯协议:核算机通讯的本质便是彼此收发字节。那么依照必定的格局收发字节便是通讯协议。


/**
* 创立客户端Socket
* Socket客户端类
* 结构的时分就会依据给定的效劳端ip地址和效劳端的端口号测验衔接
*/
try {
    System.out.println("开端衔接");
    Socket socket = new Socket("172.16.3.33", 8088);
    System.out.println("与效劳端衔接成功!");
    /**
    * 经过socket能够获取一组与效劳器通讯的输入输出流
    * 咱们对其包装就能够便利进行读写信息了。
    * 经过socket拿到的是两个初级流(字节约)
    */
    InputStream in = socket.getInputStream();
    OutputStream out = socket.getOutputStream();
    /**
    * 向效劳器发送字符,将字符输出流通换成缓冲字符输出流
    */
    PrintWriter pw = new PrintWriter(out);
    pw.println("你好效劳器!");
    pw.flush();
    /**
    * 收取效劳器发送的字符串,包装为缓冲字符输入流
    */
    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    //读取效劳器发回的信息
                                                                                                                                                                                                                                                                                                       
    String info = reader.readLine();
    System.out.println("效劳端:"+info);
} catch (IOException e) {
    //e.printStackTrace();
}
/**
* 效劳端
* 效劳器端翻开socket等候客户端的衔接
* 效劳器端的Socket名称是ServerSocket
* 创立ServerSocket需求指定效劳端口号
*/
public static void main(String[] args) {
    try {
        System.out.println("dddd");
        ServerSocket server = new ServerSocket(8050);
        /**
        * 监听端口
        * 等候客户端衔接
        * 等候客户端衔接的办法accept()
        * 该办法是一个堵塞办法,知道有客户端衔接上该办法才会回来,回来的便是当时客户端的套接字。
        * 从中咱们能够知道客户端的ip等信息。
        *
        * accept()办法能够重复调用
        */
        System.out.println("发动结束,等候衔接");
        Socket client = server.accept();
        System.out.println("有一个客户端和我衔接了");
        /**
        * 经过socket获取输入流读取客户端的信息
        */
        InputStream in = client.getInputStream();
        OutputStream out = client.getOutputStream();
                                                                                                                                                                                                                                                                                                           
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        System.out.println("客户端:"+reader.readLine());
                                                                                                                                                                                                                                                                                                           
        //向客户端发信息
        PrintWriter pw = new PrintWriter(out);
        pw.println("你好客户端");
        pw.flush();   
    } catch (IOException e) {
        //e.printStackTrace();
    }
}


2、多线程Socket


Server端多线程:

效劳器端无限循环承受客户端的拜访,每衔接都能茶圣一对新的Socket的实例。

为每个客户端衔接创立一个独立线程,处理客户恳求。


public static void main(String[] args) {
    try {
        ServerSocket server = new ServerSocket(8088);
        System.out.println("发动结束,等候衔接");
                                                                                                                                                                                                                                                                                                           
        while (true) {
        Socket client = server.accept();
        if(client==null)continue;
        System.out.println("与客户端衔接成功"+client.getInetAddress().getHostAddress());
        Handler handler = new Handler(client);//交给线程去处理
        Thread t = new Thread(handler);
        t.start();
        }   
    } catch (IOException e) {
    }
}
/**
* 与客户端通讯线程
* 担任与某个特定的socket的客户端进行通讯
* 每个线程实例担任一个客户端的通讯
*/
private static class Handler implements Runnable {
    /**
    * 当时线程要通讯的客户端socket
    */
    private Socket client;
    public Handler(Socket client) {
        this.client = client;
    }
    public void run(){
        try {
            /** 经过socket获取客户端信息 */
            InputStream in = this.client.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            /** 死循环读取客户端信息 */
                                                                                                                                                                                                                                                                                                               
            while (true) {
                if (reader.readLine() == null)
                return;
                System.out.println("客户端"+ this.client.getInetAddress().getHostAddress() + ":" + reader.readLine());
            }
        } catch (Exception e) {
        }
    }
}


3、线程池

上面这种频频的创立线程和毁掉线程是十分耗费资源和功能的。

能够创立一些空的线程,将它们保存起来,当有使命需求并发履行时,咱们取出一个空的线程来运转这个使命,当使命运转结束后,在将线程收回,等候下次分配使命。

这样自始自终咱们都只运用了开端创立的那些线程,并能够重复利用来节约功能开支。

JDK供给了线程池的管理器ExecutorService

经过Executors类的静态办法创立几个不同完成的线程池。


Executors.newCachedThreadPool();创立一个缓存线程池,当有使命的时分会查看线程池中是否有空线程,若有就运用它,若没有就创立新的线程。若持久没有运用的线程会主动收回。

Executors.newFixedThreadPool(int threads);创立一个可重用的,具有固定线程数的线程池。

Executors.newScheduledExecutor();创立只要一条线程的线程池,它能够在指定推迟后履行线程使命。


Executors.newSingleThreadExecutor();创立一个只要单线程的线程池,相当于Exceutors.newFixedThreadPool办法时传入参数1。里面维护着一个使命行列。


/**
* 创立一个线程池,具有50个
*/
ExecutorService threadPool =Executors.newFixedThreadPool(50);
try {
    ServerSocket server = new ServerSocket(8088);
    System.out.println("发动结束,等候衔接");
    /** 将转发音讯线程发动 */
    SendMessageHandler sHandler = new SendMessageHandler();
    Thread sendTh = new Thread(sHandler);
    sendTh.start();
                                                                                                                                                                                                                                                                                                       
    while (true) {
        Socket client = server.accept();
        System.out.println("与客户端衔接成功"+client.getInetAddress().getHostAddress());
        Handler handler = new Handler(client);//交给线程去处理
        /**
        * 将需求并发的使命(Runnable)交给线程池
        * 让其分配空线程去运转该使命
        * 若线程都在作业,那么登载,直到有空线程停止。
        */
        threadPool.execute(handler);
        //Thread t = new Thread(handler);
        //t.start();
    }
} catch (IOException e) {
}


4、双端行列

内部由两个行列完成,他们替换进行存取作业。这样至少能够确保2个线程一起进行存取操作。可是关于两个线程一起进行存或许取,仍是要求同步的。

可是比单行列完成线程安全仍是要快的。


BlockingDeque双端行列

完成:

1)ArrayBlockingDeque该类完成的结构办法要求咱们传入一个整数,代表当时行列的长度。所以这个是一个固定巨细的双端行列,存取准则为FIFO先入先出的准则。

当元素调用offer存入了行列时,若行列已满,那么能够设置延时等候,当超过了延时等候会回来存入失利。

2)LinkedBlockingDeque变长双端行列。长度不定,跟着元素数量而添加,最大能够到达Integer.MAX_VALUE,重载结构办法能够传入一个整数,使之变为一个定长的行列。

3)PriorityBlockingDeque 这个和LinkedBlockingDeque类似,只不过是进去了天然排序后获取。

4)SynchronousQueue特别的双端行列 存取过程有要求,有必要存一次取一次,替换进行。

例:

效劳端

/**
* 创立一个静态调集维护一切客户端的数输入流
* 留意:由于这个调集被多个线程运用,所哟一调集要是安全的。
*/
static Vector<PrintWriter> allOut = new Vector<PrintWriter>();
/** 音讯行列 */
static BlockingDeque<String> msgQueue = new LinkedBlockingDeque<String>();
public static void main(String[] args) {
    /**
    * 创立一个线程池,具有50个
    */
    ExecutorService threadPool =Executors.newFixedThreadPool(50);
    try {
        ServerSocket server = new ServerSocket(8088);
        System.out.println("发动结束,等候衔接");
        /** 将转发音讯线程发动 */
        SendMessageHandler sHandler = new SendMessageHandler();
        Thread sendTh = new Thread(sHandler);
        sendTh.start();
                                    
        while (true) {
            Socket client = server.accept();
            System.out.println("与客户端衔接成功"+client.getInetAddress().getHostAddress());
            Handler handler = new Handler(client);//交给线程去处理
            /**
            * 将需求并发的使命(Runnable)交给线程池
            * 让其分配空线程去运转该使命
            * 若线程都在作业,那么登载,直到有空线程停止。
            */
            threadPool.execute(handler);
            //Thread t = new Thread(handler);
            //t.start();
        }
    } catch (IOException e) {
    }
}
/**
* 与客户端通讯线程
* 担任与某个特定的socket的客户端进行通讯
* 每个线程实例担任一个客户端的通讯
*/
private static class Handler implements Runnable {
    /**
    * 当时线程要通讯的客户端socket
    */
    private Socket client;
                                
    public Handler(Socket client) {
        this.client = client;
    }
    public void run(){
        PrintWriter pw = null;
        try {
            /** 经过socket获取客户端信息 */
            InputStream in = this.client.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            /**
            * 将当时客户端的输出流保存到同享调集
            */
            pw = new PrintWriter(client.getOutputStream());
            allOut.add(pw);
            /** 死循环读取客户端信息 */
            while (true) {
                String str = reader.readLine();
                /**
                * 将信息放入到音讯行列
                */
                if("q".equals(str)){
                client.close();
                }
                msgQueue.offer(str);
                /**
                * arg1:寄存的元素
                * arg2:延时时刻
                * arg3:延时的时刻单位
                *
                * 下面办法是向行列中寄存元素,设置5秒超时,若超时了还没有放入行列这回来false
                */
                boolean tf = msgQueue.offer(str, 5, TimeUnit.SECONDS);
            }       
        } catch (Exception e) {
            e.printStackTrace();
            /**
            * 抛出反常,咱们应该将这个客户端的输出流从同享调集中删去
            * 通知其他线程不要再发信息了。
            */
            allOut.remove(pw);
        }
   }
}
/**
* 转发音讯
* 循环音讯行列,将每一个音讯经过一切客户端的输入流发送给客户端
* 做到播送的作用。
*/
public static class SendMessageHandler implements Runnable{
    public void run() {
        while (true) {
            /** 循环将音讯发送给每一个客户端 每50ms作一次*/
            String str = null;
            while ((str=msgQueue.poll())!=null) {//and msgQueue.poll()!=null
                /** 获取一切客户端的输出流,将字符串输出 */
                for (PrintWriter pw : allOut) {
                    pw.println(str);
                    pw.flush();
                }
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


客户端:

public static void main(String[] args) {
    /**
    * 创立客户端Socket
    * Socket客户端类
    * 结构的时分就会依据给定的效劳端ip地址和效劳端的端口号测验衔接
    */
    try {
        System.out.println("开端衔接");
        Socket socket = new Socket("172.16.3.14", 8088);
        System.out.println("与效劳端衔接成功!");
              
        //发动音讯线程
        Thread readerMsgTh = new Thread(new ReadMessageHandler(socket.getInputStream()));
        readerMsgTh.start();
              
        OutputStream out = socket.getOutputStream();
        PrintWriter pw = new PrintWriter(out);
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true) {
            pw.println(reader.readLine());
            pw.flush();
        }
    } catch (IOException e) {
    //e.printStackTrace();
    }
}
    /**
    * 该线程用于从效劳器读取信息。
    */
    public static class ReadMessageHandler implements Runnable{
        private InputStream in;//从效劳端读取信息
        public ReadMessageHandler(InputStream in) {
            this.in = in;
        }
        public void run() {
            try {
                //将字节输入流通换为缓冲字符输入流
                BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                while (true) {
                   System.out.println(reader.readLine());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }



http://www.cnblogs.com/springcsc/archive/2009/12/03/1616413.html这里有一篇文章讲的比较细,把地址记录下来,供查询,感谢作者。


版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表凯时娱乐立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章