java网络编程复习

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/angle_chen123/article/details/80941872

1、多线程的并发同步?

(1)多线程的同步:线程同步就是当线程发出一个功能调用时,在没有有得到结果之前,该调用不会返回,其他线程也不能调用该方法。

(2)并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。其中两种关系分别是同步和互斥。

同步就是进城之间的关系不是相互排斥临界资源的关系,而是相互依赖的关系。金玉不说明就是:就是前一个进程的输出就是下一个进程的输入,当第一个进程没有输出时第二个进程必须等待。具有同步关系的一组并发进程相互发送的信息称为消息或事件。

2、什么是多线程?

多线程是指一个应用或者程序同时执行多个任务,一般来说一个任务就是一个线程,而一个应用程序有一个以上的线程我们称之为多线程。

线程的状态转换:


多线程计算示例:

(1)数组求和

    for (int i = 0; i < threadCount; i++) {  
         final int index = i;  
     new Thread() {  
        public void run() {  
        long s = 0;  
        int start = index * lenPerThread;  
        int end = start + lenPerThread;  
       for (int j = start; j < end; j++) {  s += array[j];  }  
       synchronized (rd) {  
                        rd.sum += s;  
                        rd.finishThreadCount++;  }  
                };  
            }.start();  

        } 

(2)蒙特卡洛求解PI

double Darts(int n)
{ // 用随机投点法计算PI值
    RandomNumber rnd;
    int k=0;
    for (int i=1;i <=n;i++) {
      double x=rnd.fRandom();
      double y=rnd.fRandom();
      if ((x*x+y*y)<=1) k++;
      }
    return 4*k/double(n);

}

3、并行程序编写及伪共享

    编写优质的并发(并行)程序是一件难度极高的事情。并发(并行)可以明显改进应用程序的吞吐量(获得更多的CPU调度时间)和结构(程序有多个部分在协同工作),提高程序运行效率。结合网络通信,可以实现更复杂的分布式并行算法。

    在多核的CPU架构中,每一个核心core都会有自己的缓存空间,因此如果一个变量如果同时存在不同的核心缓存空间时,就会出现伪共享(false sharing)的问题。

并行算法避免伪共享


4、什么是fork/join模型?

(1)一种并行计算的多线程编程模型

(2)开始-->任务分割-->多线程异步执行-->任务合并-->阻塞等待合并结果(是一种分治算法,将一个大的任务分成多个相同的小的模块进行计算)

(3)work-stealing算法:

    每个线程维护一个各自的双端的链表,有新任务时之间插入的前端优先执行,前端无任务时,窃取其他线程双端链表的任务加入到自己的尾端进行处理。

    通常情况下,并发的线池都是维护一个共享任务的任务队列,新任务到来时插入到队列的尾部,而线程执行任务时取队列的首部任务,而forkjoin编程成模型刚好相反,优先处理新任务,新任务放在最前面优先执行,自己的链表里面没有任务时,在执行尾部的任务。

5、什么是线程池及线程池的优点?

    线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中的数量取决于可用内存数量和应用程序的需求。然而,增加可用线程数量是可能的,线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。

    优点:(1)提高资源利用率,线程池可以重复利用已经创建的线程。

                (2)提高响应速度。当需要一个线程时如果线程池中有直接使用,不需要创建,减少了创建的时间。

                (3)具有可管理性,线程池会根据当前系统的特点对池内的线程进行优化处理。

完成一个线程池的工作线程

(1)线程池

private boolean isClosed=false;  //线程池是否关闭
 private LinkedList<Runnable> workQueue;  //表示任务队列
 public ThreadPool(int poolSize) { 
      //poolSize指定线程池中的工作线程数目
    setDaemon(true);
    workQueue = new LinkedList<Runnable>();  //创建工作队列
    for (int i=0; i<poolSize; i++)
      new WorkThread().start();  //创建并启动工作线程

  }

(2)execute

public synchronized void execute(Runnable task) {
    if (isClosed) { //线程池被关则抛出IllegalStateException异常
      throw new IllegalStateException();
    }
    if (task != null) {
      workQueue.add(task);
      notify();  //唤醒正在getTask()方法中等待任务的工作线程
    }

  }

(3)getTask

protected synchronized Runnable getTask() {
    while (workQueue.size() == 0) {
      if (isClosed) return null;
      wait();  //如果工作队列中没有任务,就等待任务
    }
    return workQueue.removeFirst();

  }

(4)WorkThread

private class WorkThread extends Thread {
       //  在此省略构造函数………………………..
    public void run() {
      while (!isInterrupted()) { 
        Runnable task = null;
        try {
            task = getTask();
            if (task == null) return; 
            task.run();   
         }   
        catch (Throwable t) {   t.printStackTrace();        }
      }//#while
    }//#run()
  }//#WorkThread类

6、面向链接、面向无连接socket通信的区别?

    面向链接的Socket编程就是基于TCP的Socket编程。

    面向无连接的Socket编程就是基于UDP的socket编程。

(1)在socket函数创建时函数参数不。TCP通讯我们通常填SOCK_STREAM,这种的特点是面向连接的,即每次收发数据之前必须通过connect建立连接,也是双向的,即任何一方都可以收发数据,协议本身提供了一些保障机制,保证它是可靠的、有序的,即每个包按照发送顺序到达接收方。

而UDP通讯常用SOCK_DGRAM作为参数,它是无连接的,不可靠的,因为通讯双方发送数据后不知道对方是否已经接收到数据,是否正常收到数据任何一方建立一个socket以后就可以用sendto发送数据,也可以用recvfrom接收数据。根本不关心对方是否存存在,是否发送了数据。它的特点是通讯速度比较快。

(2)编程步骤不同

TCP编程的服务端:

1、创建一个socket,用函数socket();

2、设置socket属性,用函数setsockopt();

3、绑定IP地址、端口等信息到socket上,用函数bind();

4、开启监听,用函数listen();

5、接收客户端上来的连接,用函数accept();

6、收发数据,用函数send()和recv(),或者read()和write();

7、关闭网络连接

8、关闭监听

TCP客户端的一般步骤:

1、创建一个socket,用函数socket();

2、设置socket属性,用函数setsockopt();

3、绑定IP地址、端口等信息到socket上,用函数bind();

4、设置要连接的对方的IP地址和端口等属性;

5、连接服务器,用函数connect();

6、收发数据,用函数send()和recv().或者read()和write();

7、关闭网络来连接;

UDP编程的服务器端一般步骤:

1、创建一个socket,使用socket()函数;

2、设置socket属性,用函数setsockopt();

3、绑定IP地址、端口等信息到socket上,用函数bind();

4、循环接收数据,用函数recvfrom();

5、关闭网络连接

UDP编程的客户端一般步骤:

1、创建衣蛾socket,用socket()函数;

2、设置socket属性,用函数setsockopt();

3、绑定IP地址、端口等信息到socket上,用函数bind()

4、设置对方的IP地址和端口信息等属性;

5、发送数据,用函数sendto();

6、关闭网络连接

7、如何实现一个群组客户端网络通信?

传统web请求和响应模式中,浏览器通过http仅能实现单向的通信,comet可以一定程度上模拟双向通信,但是效率低,并需要服务端有较好的支持。而WebSocket一旦在浏览器中的得到实现,将会代替上面两项技术,它能更好的节省服务器资源和带宽并达到实时通讯。webSocket是实现管道式的实时通信,打开一个浏览器和服务器的通信通道,持续连接。服务器给浏览推送数据,非常方便。

https://blog.csdn.net/qq_36899469/article/details/78477217

8、NIO中的缓冲区、通道是什么?

缓冲区:

    缓冲区是一个用于特定基本数据类型的容器。由java.nio包定义的,所有缓冲区都是Buffer抽象的子类,Buffer主要用于与NIO通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。  缓冲区是通道内部发送数据和接收数据的端点。

通道:

    通道主要是用于传输数数据,从缓冲中区的一侧传到另一侧的实体,通道是访问IO服务的导管,通过通道,可以最小的开销来访问操作系统的I/O服务

9、缓冲区容量、极限、位置关系

(1) 容量(capacity):表示该缓冲区可以保存多少数据。
(2) 极限(limit):表示缓冲区的当前终点,不能对缓冲区中超过极限的区域进行读写操作。
(3) 位置(position):表示缓冲区中下一个读写单元的位置。
    以上三个属性的关系为:容量>=极限>=位置>=0

缓冲区提供了用于改变以上三个属性的方法:
clear():把极限设为容量,再把位置设为0
flip():把极限设为位置,再把位置设为0 

rewind():不改变极限,把位置设为0

10、使用NIO中的通道,它的通信流程是什么?有什么优点?

(1) ReadableByteChannel接口:声明了read(ByteBuffer dst)方法,该方法把数据源的数据读入到参数指定的ByteBuffer缓冲区中。
(2)WritableByteChannel接口:声明了write(ByteBuffer src)方法,该方法把参数指定的ByteBuffer缓冲区中的数据写到数据汇中。
(3)FileChannel类:是Channel接口的实现类,代表一个与文件相连的通道。
(4)SelectableChannel类:也是一种通道,它不仅支持阻塞的I/O操作,还支持非阻塞的I/O操作。

优点:

(1)更灵活的可伸缩的IO接口,包括,IO抽象Channela的出现以及显得多元。可以更灵活方便构建产品级别的服务。

(2)快速缓冲二进制IO API使得用户可以容易的编写出操作文件流或者二进制数据流的高性能代码。

(3)字符集解码编码转换的API使得用户可以直接访问操作系统内置的字符集转换器。

(4)可以使用户更加有针对性的来处理各种IO错误,让用户能够在各种平台上一致的对待这些错误。

(5)增加了并发的支持,NIO类中的大部分都支持多个并发的线程。

11、同步通信与异步通信的区别是什么?

同步通信是指甲方向乙方发送了一批数据后,必须等接收到了乙方的响应数据后,再发送下一批数据。
异步通信是指发送数据和接收数据的操作互不干扰,各自独立进行。

区别:

(1)同步通信要求一个IO操作完成之后,才能完成下一个IO操作,用阻塞模式更容易实现。异步通信允许发送数据和接收数据的操作各自独立进行,用非阻塞模式更容易实现它。
(2)同步通信要求接收端时钟频率和发送端的时钟频率一致,发送端发送连续的比特流。异步通信时不要求接收端和发送端时钟同步,发送端发送完一个字节后,可以经过任意长的时间间隔再发送下一个字节。

(3)同步通信效率高,异步通信效率低

(4)同步通信比较复杂,双方时钟的允许误差较小;异步通信简单,双方时钟可允许一定误差。

(5)同步通信可用于点对多点;异步通信只能用于点对点。

注意:通信两端可以使用不同的通信方式,一方采用同步通信方式,一方可以采用异步通信方式。

12、在通信过程中导致阻塞发生的原因是什么?

(1)线程执行了Thread.sleep(int n)方法,线程放弃CPU,睡眠n毫秒,然后恢复运行。
(2)线程要执行一段同步代码,由于无法获得相关的同步锁,只好进入阻塞状态,等到获得了同步锁,才能恢复运行。
(3)线程执行了一个对象的wait()方法,进入阻塞状态,只有等到其他线程执行了该对象的notify()或notifyAll()方法,才可能将其唤醒。

(4)线程执行I/O操作或进行远程通信时,会因为等待相关的资源而进入阻塞状态。(例如建立连接、数据的输入输出过程都有可能导致线程进入阻塞状态)

13、什么是反射?他有什么作用?

        反射主要是指程序可以访问、检测和修改他本身状态或者行为的一种能力,并能根据自身行为的状态和结果,聊调整或修改应用所描述行为的状态和相关的语义。反射是Java中一强大的工具,能够使我们很方便的创建代码,这些代码在运行时装配,无需在组件之间进行源代码链接。

  作用:

(1)在运行时判断任意一个对象所属的类型

(2)在运行时构造任意一个类的对象

(3)在运行判断任意一个类所具有的成员变量和方法

(4)在运行时调用任意一个对象的方法,甚至可以调用private 方法

14、静态代理和动态代理的区别

静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理类:在程序运行时,运用反射机制动态创建而成。

静态代理实现:

(1)接口实现

HelloService.java
package proxy;
import java.util.Date;
public interface HelloService{
  public String echo(String msg);
  public Date getTime();
}

(2)实现类

import java.util.Date;
public class HelloServiceProxy implements HelloService{
        private HelloService helloService;
        public HelloServiceProxy(HelloService helloService){    this.helloService=helloService;  }
        public void setHelloServiceProxy(HelloService helloService){    this.helloService=helloService;  }
        public String echo(String msg){
                  System.out.println("before calling echo()");
                   String result=helloService.echo(msg);
                    System.out.println("after calling echo()");
                   return result;
           }
         public Date getTime(){
                   System.out.println("before calling getTime()");
                   Date date=helloService.getTime();
                   System.out.println("after calling getTime()");
                   return date;
          }
}

(3)客户端实现

package proxy;
public class Client1{
  public static void main(String args[]){
    HelloService helloService=new HelloServiceImpl();
    HelloService helloServiceProxy=new HelloServiceProxy(helloService);
    System.out.println(helloServiceProxy.echo("hello"));
  }
}

动态代理实现:

(1)客户端:

package proxy;
public class Client2{
  public static void main(String args[]){
    HelloService helloService=new HelloServiceImpl();
    HelloService helloServiceProxy=
               HelloServiceProxyFactory.getHelloServiceProxy(helloService);
    System.out.println("动态代理类的名字为"
                       +helloServiceProxy.getClass().getName());
    System.out.println(helloServiceProxy.echo("Hello"));
  }
}

(2)cjlib动态代理

public class BookFacadeCglib implements MethodInterceptor {  
      private Object target;//业务类对象,供代理方法中进行真正的业务方法调用  
      //相当于JDK动态代理中的绑定
      public Object getInstance(Object target) {  
            this.target = target;  //给业务对象赋值
            Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类
            enhancer.setSuperclass(this.target.getClass());  //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
             // 创建动态代理类对象并返回  
            return enhancer.create(); 
      }
    // 实现回调方法 
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
          System.out.println("预处理——————");
          proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法
          System.out.println("调用后操作——————");
          return null; 
      } 
}

public static void main(String[] args) {      
        BookFacadeImpl bookFacade=new BookFacadeImpl1();
        BookFacadeCglib  cglib=new BookFacadeCglib();  
           BookFacadeImpl bookCglib=(BookFacadeImpl)cglib.getInstance(bookFacade);  
        bookCglib.addBook();  
    }

创建代理类

 public static void main(String args[])throws Exception {
             //创建静态代理类实例
        HelloService helloService1=new HelloServiceProxy("localhost",8000);
        System.out.println(helloService1.echo("hello"));
        System.out.println(helloService1.getTime());
        //创建动态代理类实例
        HelloService helloService2=
             (HelloService)ProxyFactory.getProxy(HelloService.class,"localhost",8000);
        System.out.println(helloService2.echo("hello"));
        System.out.println(helloService2.getTime());

    }

15、什么是远程方法调用?

java远程方法调用时一个Java API,它执行的面向对象的等价远程调用的方法,包括了直接传输序列化的Java类和分布式垃圾收集的支持。远程调用方法,也可以看作是一个远程运行的对象上激活的方法的过程。它提供位置透明性,因为用户认为一个方法是在本地运行的对象上执行。

16、什么是数据库连接池,它有什么优点?

数据库连接池是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,有程序动态地对池中的连接进行申请、使用、释放。

优点:

(1)资源重用。由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性。

(2)更快的系统响应速度。数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作已经完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。

(3)新的资源分配手段。对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接配置,实现数据库连接池技术。某一个应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。

(4)统一的连接管理,避免数据库连接泄漏。在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。

17、web框架中Spring MVC主要特点,其中M、V、C中各有什么组件?

(1)清晰的角色划分:控制器(controller)、验证器(validator)、命令对象(command obect)、表单对象(form object)、模型对象(model object)、Servlet分发器(DispatcherServlet)、处理器映射(handler mapping)、试图解析器(view resoler)等等。每一个角色都可以由一个专门的对象来实现。
(2)强大而直接的配置方式:将框架类和应用程序类都能作为JavaBean配置,支持跨多个context的引用,例如,在web控制器中对业务对象和验证器validator)的引用。
(3)可适配、非侵入:可以根据不同的应用场景,选择何事的控制器子类(simple型、command型、from型、wizard型、multi-action型或者自定义),而不是一个单一控制器(比如Action/ActionForm)继承。
(4)可重用的业务代码:可以使用现有的业务对象作为命令或表单对象,而不需要去扩展某个特定框架的基类。
(5)可定制的绑定(binding)和验证(validation):比如将类型不匹配作为应用级的验证错误,这可以保证错误的值。再比如本地化的日期和数字绑定等等。在其他某些框架中,你只能使用字符串表单对象,需要手动解析它并转换到业务对象。
(6)可定制的handler mapping和view resolution:Spring提供从最简单的URL映射,到复杂的、专用的定制策略。与某些web MVC框架强制开发人员使用单一特定技术相比,Spring显得更加灵活。
(7)灵活的model转换:在Springweb框架中,使用基于Map的键/值对来达到轻易的与各种视图技术集成。/
(8)可定制的本地化和主题(theme)解析:支持在JSP中可选择地使用Spring标签库、支持JSTL、支持Velocity(不需要额外的中间层)等等。
(9)简单而强大的JSP标签库(Spring Tag Library):支持包括诸如数据绑定和主题(theme)之类的许多功能。他提供在标记方面的最大灵活性。/
(10)JSP表单标签库:在Spring2.0中引入的表单标签库,使用在JSP编写表单更加容易。
(11)Spring Bean的生命周期可以被限制在当前的HTTp Request或者HTTp Session。准确的说,这并非Spring MVC框架本身特性,而应归属于Spring MVC使用的WebApplicationContext容器。
MVC模型的设计模式在Spring MVC中各个组件之间的对应关系

M:(Model(模型)):企业数据和业务规则,业务逻辑处理。与之对应的组件有:HandlerMapping(处理器映射器),负责查找URL;HandlerAdapter(处理器适配器)负责执行Handler;Handler(处理器),对各种请求进行处理。

V:(View(视图)):是用户看到并与之交互的界面,视图没有正真的处理发生。对应组件:ViewResolver(视图解析器)解析物理视图,返回逻辑视图;View(视图),渲染视图,返回视图

C:(Controller(控制)):接收用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理,他只是接收请求并决定调用哪个模型构件去处理请求。对应组件:DispatcherServlet(前端控制器),负责请求request和响应response。

18、群组发送服务端代码实现

package com.bjsxt.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import com.bjsxt.vo.ContentVo;
import com.bjsxt.vo.Message;
import com.google.gson.Gson;

/**
 * 总通信管道
 * @author xiongzichao
 *
 */
@ServerEndpoint("/chatSocket")
public class ChatSocket {

    //定义一个全局变量集合sockets,用户存放每个登录用户的通信管道
    private  static  Set<ChatSocket>  sockets=new HashSet<ChatSocket>();
    //定义一个全局变量Session,用于存放登录用户的用户名
    private  Session  session;
    //定义一个全局变量map,key为用户名,该用户对应的session为value
    private  static  Map<String, Session>  map=new HashMap<String, Session>();
    //定义一个数组,用于存放所有的登录用户,显示在聊天页面的用户列表栏中
    private  static  List<String>names=new ArrayList<String>();
    private String username;
    private Gson  gson=new Gson();

    /*
     * 监听用户登录
     */
    @OnOpen
    public void open(Session session){
        this.session = session;
        //将当前连接上的用户session信息全部存到scokets中
        sockets.add(this);
        //拿到URL路径后面所有的参数信息
        String queryString = session.getQueryString();
        System.out.println();
        //截取=后面的参数信息(用户名),将参数信息赋值给全局的用户名
        this.username = queryString.substring(queryString.indexOf("=")+1);
        //每登录一个用户,就将该用户名存入到names数组中,用于刷新好友列表
        names.add(this.username);
        //将当前登录用户以及对应的session存入到map中
        this.map.put(this.username, this.session);
        System.out.println("用户"+this.username+"进入聊天室");
        Message message = new Message();
        message.setAlert("用户"+this.username+"进入聊天室");
        //将当前所有登录用户存入到message中,用于广播发送到聊天页面
        message.setNames(names);
        //将聊天信息广播给所有通信管道(sockets)
        broadcast(sockets, gson.toJson(message) );
    }

    /*
     * 退出登录
     */
    @OnClose
    public void close(Session session){
        //移除退出登录用户的通信管道
        sockets.remove(this);
        //将用户名从names中剔除,用于刷新好友列表
        names.remove(this.username);
        Message message = new Message();
        System.out.println("用户"+this.username+"退出聊天室");
        message.setAlert(this.username+"退出当前聊天室!!!");
        //刷新好友列表
        message.setNames(names);
        broadcast(sockets, gson.toJson(message));
    }

    /*
     * 接收客户端发送过来的消息,然后判断是广播还是单聊
     */
    @OnMessage
    public void receive(Session  session,String msg) throws IOException{
        //将客户端消息转成json对象
        ContentVo vo = gson.fromJson(msg, ContentVo.class);
        //如果是群聊,就像消息广播给所有人
        if(vo.getType()==1){
            Message message = new Message();
            message.setDate(new Date().toLocaleString());
            message.setFrom(this.username);
            message.setSendMsg(vo.getMsg());
            broadcast(sockets, gson.toJson(message));
        }else{
            Message message = new Message();
            message.setDate(new Date().toLocaleString());
            message.setFrom(this.username);
            message.setAlert(vo.getMsg());
            message.setSendMsg("<font color=red>正在私聊你:</font>"+vo.getMsg());
            String to  = vo.getTo();
            //根据单聊对象的名称拿到要单聊对象的Session
            Session to_session = this.map.get(to);
            //如果是单聊,就将消息发送给对方
            to_session.getBasicRemote().sendText(gson.toJson(message));
        }
    }

    /*
     * 广播消息
     */
    public void broadcast(Set<ChatSocket>sockets ,String msg){
        //遍历当前所有的连接管道,将通知信息发送给每一个管道
        for(ChatSocket socket : sockets){
            try {
                //通过session发送信息
                socket.session.getBasicRemote().sendText(msg);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

19、使用NIO通信完成文件传送

package channel;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
import org.junit.Test;
 
public class TestNIO {
	
	@Test
	public void client(){
		try {
			SocketChannel sc = SocketChannel.
					open(new InetSocketAddress("127.0.0.1", 8080));
			//设置为非阻塞模式
			sc.configureBlocking(false);
			FileInputStream fis = new FileInputStream(new File("1.jpg"));
			FileChannel inChannel = fis.getChannel();			
			ByteBuffer dst = ByteBuffer.allocate(1024);
			while(inChannel.read(dst) != -1){
				dst.flip();
				sc.write(dst);
				dst.clear();
			}
			inChannel.close();
			fis.close();
			sc.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}		
	}
	
	@Test
	public void test2(){
		try {
			SocketChannel sc  = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080));
			sc.configureBlocking(false);
			ByteBuffer src = ByteBuffer.allocate(1024);
			src.put(new Date().toString().getBytes());
			src.flip();
			sc.write(src);
			src.clear();
			sc.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@Test
	public void server(){
		try {
			ServerSocketChannel ssc = ServerSocketChannel.open();//1
			//设置为非阻塞模式
			ssc.configureBlocking(false);//2
			ssc.bind(new InetSocketAddress(8080));//3
			ByteBuffer buf = ByteBuffer.allocate(1024);			
			FileOutputStream fos = new FileOutputStream(new File("2.jpg"));
			FileChannel outChannel = fos.getChannel();
			SocketChannel sc = null;
			//用open()获取选择器
			Selector selector = Selector.open();//4
			/**注册通道到选择器上,指定监控状态(SelectionKey选择键)
			 * SelectionKey.OP_ACCEPT---接收事件
			 * SelectionKey.OP_CONNECT---连接事件
			 * SelectionKey.OP_READ---读事件
			 * SelectionKey.OP_WRITE---写事件
			 * 若监听事件不止一个时,可以使用"位或"操作符连接;
				int ops = SelectionKey.OP_ACCEPT | SelectionKey.OP_READ;
			 */			
			ssc.register(selector, SelectionKey.OP_ACCEPT);//5
			//轮询获取选择器上准备就序的监听事件
			while(selector.select() > 0){//selector.select()>0说明至少有一个通道准备就序
				Set<SelectionKey> keys = selector.selectedKeys();
				Iterator<SelectionKey> its = keys.iterator();
				while(its.hasNext()){
					SelectionKey selectionKey = its.next();
					//遍历所有准备就序的监听事件SelectionKey,并进行判断
					if(selectionKey.isAcceptable()){//可以启动单独的线程来处理
						//若接收就序就打开服务端连接
						sc = ssc.accept();
						sc.configureBlocking(false);
						sc.register(selector, SelectionKey.OP_READ);						
						//...后序读操作
					}else if(selectionKey.isConnectable()){
						//可以启动单独的线程来处理
						
					}else if(selectionKey.isReadable()){//可以启动单独的线程来处理
						sc = (SocketChannel) selectionKey.channel();
						//sc.configureBlocking(false);
						//sc.register(selector, SelectionKey.OP_READ);
						int len = -1;
						while((len = sc.read(buf))!= -1){
							buf.flip();
							outChannel.write(buf);
							buf.clear();				
						}
					}else if(selectionKey.isWritable()){
						//可以启动单独的线程来处理
					}
				}
				//selectionKey用完后取消掉
				its.remove();
			}
			
			outChannel.close();
			fos.close();
			sc.close();
			ssc.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

20、完成socket简单的服务端客户端通信

(1)服务端

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class server_1 {  
    public static void main(String[] args) throws IOException { //将异常全部抛出 
        ServerSocket server = new ServerSocket(10000);    //用serversocket来启动服务器,并指定端口号
        System.out.println("服务器启动。。。");
    
            Socket socket = server.accept();  //获取客户端的socket信息
            BufferedReader in = null;  
            in = new BufferedReader(new InputStreamReader(socket.getInputStream())); //启动缓冲区 
            while (true) {  
                  String msg = in.readLine(); // 将客户端发送来的信息存储在msg中 
                  System.out.println(msg);   
                  if (msg.equals("shutdown")) {  //客户端如果发送的是shutdown,就关闭客户端
                        break;  
                  }  
             }
             in.close(); //执行相应的关闭操作                 
    }  
}

(2)客户端

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
 
public class client_1 {  
    public static void main(String[] args) throws Exception {  
    	System.out.println("客户端启动");
        Socket socket = new Socket("localhost", 10000);  //启动socket,并连接本地主机的相应端口号
        PrintWriter out = new PrintWriter(socket.getOutputStream());  
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));//从控制台获取输入的内容  
        //不断的获取输入的内容,并不断的发送给服务器,当输入shutdown时,跳出循环,停止运行
        while (true) {  
            String msg = reader.readLine();  
            out.println(msg);  
            out.flush();  
            if (msg.equals("shutdown")) {  
                break;  
            }  
        }  
        //执行相应的关闭操作
        socket.close();
        out.close();
        reader.close();
    }  
}  

21、应用反射解析一个类获取其中的方法、属性

public class Demo02 {
	@SuppressWarnings("all")
	public static void main(String[] args) throws Exception {
		
		try {
			Class clazz = Class.forName(“User”);
 
			// 获取类名
			String strName01 = clazz.getName();// 获取完整类名com.sg.myReflection.bean.User
			String strName02 = clazz.getSimpleName();// 直接获取类名 User
 
			// 获取属性
			Field[] field01 = clazz.getFields(); // 返回属性为public的字段
			Field[] field02 = clazz.getDeclaredFields(); // 返回所有的属性
			Field field03 = clazz.getDeclaredField("id"); // 获取属性为id的字段
 
			// 获取普通方法
			Method[] Method01 = clazz.getDeclaredMethods(); // 返回public方法
			Method method = clazz.getDeclaredMethod("getId", null); // 返回getId这个方法,如果没有参数,就默认为null
 
			
			// 获取构造方法
			User u1 = (User) clazz.newInstance(); // 获取无参的构造函数这里的前提的保证类中应该有无参的构造函数
			// 获取参数为(int,String,int)的构造函数
			Constructor c2 = clazz.getDeclaredConstructor(int.class, String.class, int.class);
			// 通过有参构造函数创建对象
			User u2 = (User) c2.newInstance(1001, "小小", 18);
 
			
			// 通过反射调用普通方法
			User u3 = (User) clazz.newInstance();
			Method method03 = clazz.getDeclaredMethod("setId", int.class);
			method.invoke(u3, 1002); // 把对象u3的id设置为1002
 
			
			
			// 通过反射操作普通的属性
			User u4 = (User) clazz.newInstance();
			Field f = clazz.getDeclaredField("name");
			f.setAccessible(true); // 设置属性可以直接的进行访问
			f.set(u4, "石头");
 
		} catch (ClassNotFoundException e) {
            e.printStackTrace();
		}
	}
}

解析的类:

public class User {
	// 学号
	public int id;
	// 名字
	String name;
	// 年龄
	int age;
 
	public User() {
		super();
	}
 
	public User(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}
 
	public int getId() {
		return id;
	}
 
	public void setId(int id) {
		this.id = id;
	}
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	public int getAge() {
		return age;
	}
 
	public void setAge(int age) {
		this.age = age;
	}
 
}

21、用NIO实现文件拷贝

package part01;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class CopyFile03 {

    public static void main(String[] args) throws Exception
    {
        FileInputStream fi=new FileInputStream("d:\\tools\\Camtasia_Studio_8.6.zip");
        FileOutputStream fo=new FileOutputStream("c:\\Camtasia_Studio_8.6.zip");


        FileChannel fic=fi.getChannel();
        FileChannel  foc=fo.getChannel();

        ByteBuffer buffer= ByteBuffer.allocate(4096);

        int len=-1;

        while((len=fic.read(buffer))>0)
        {
            buffer.flip();
            foc.write(buffer);
            buffer.clear();
        }
        fic.close();
        foc.close();
        System.out.println("Ok");

    }

}

猜你喜欢

转载自blog.csdn.net/angle_chen123/article/details/80941872