CoreJava学习11——网络编程

Java网络编程html

一、Socket编程java

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

网络通讯模型编程

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();
    }
}


二、多线程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) {
        }
    }
}


三、线程池

上面这种频繁的建立线程和销毁线程是很是消耗资源和性能的。

能够建立一些空的线程,将它们保存起来,当有任务须要并发执行时,咱们取出一个空的线程来运行这个任务,当任务运行完毕后,在将线程收回,等待下次分配任务。

这样自始自终咱们都只使用了开始建立的那些线程,并能够重复利用来节省性能开销。

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) {
}


四、双端队列

内部由两个队列实现,他们交替进行存取工做。这样至少能够保证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这里有一篇文章讲的比较细,把地址记录下来,供查询,感谢做者。

相关文章
相关标签/搜索