【序列化+反序列化+反射+网络聊天室】

5 篇文章 0 订阅
3 篇文章 0 订阅

一、序列化+反序列化

1.1序列化+反序列化概念

/**
 * 序列化:就是使用ObjectOutputStream将一个Java对象的信息写入流中
 *
 *              对象--->数据流(流数据)
 * 构造方法:    public ObjectOutputStream(OutputStream out)
 * 成员方法:public final void writeObject(Object obj) :将指定的对象写入到序列化流中,变成流数据
 *
 *
 * 反系列化:就是使用ObjectInputStream 将数据流中的内容--->还原成--->对象
 * 构造方法:
 *      public ObjectInputStream(InputStream in)
 * 成员方法:
 *      public final Object readObject():从反序列流中将数据流的内容----解析成对象
 *
 */

1.2 代码实现

public class ObjectStream {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
       // write() ;

        read() ;
    }

    //读:将流数据---还原成--->对象
    public static void read() throws IOException, ClassNotFoundException {
        //创建反序列化流对象ObjectInputStream
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")) ;

        // public final Object readObject():从反序列流中将数据流的内容----解析成对象
        Object object = ois.readObject();

        //强转学生类型
        Student s = (Student) object;
        System.out.println(s.getName()+"---"+s.getAge());

    }

    //写数据
    public static void write() throws IOException {
        //创建序列化流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt")) ;
        //public final void writeObject(Object obj) :将指定的对象写入到序列化流中,变成流数据

        //创建一个学生对象
        Student s = new Student("高圆圆",44) ;

        oos.writeObject(s) ;
        //java.io.NotSerializableException: com.qf.io_01.Student  当前这个类未实现序列化异常

    }
}
package com.qf.io_01;

import java.io.Serializable;

/**
 * 学生类
 *当没有去实现Serializable接口时,会出现.NotSerializableException异常!
 * 只有支持java.io.Serializable接口的对象才能写入流中
 * 当前这个类能够实现java.io.Serializable接口---->这个类对象就可以被序列化(对象---流数据)
 * java提供一些接口:如果字段,连成员方法都没有,都称为"标记接口"
 *
 *
 * 序列化的时候--->当前类的所有的成员(成员变量/成员方法/构造方法等等)---->内存中--->"类的签名信息" 序列化的ID值
 * 如果反序列化的时候,修改了类的成员信息,会导致和之前序列化的信息不一致:就会出现
 * java.io.InvalidClassException:序列化中的序列化版本UID值和反序列化的UID值不相同(类的签名信息不一致)
 *
 */
public class Student implements Serializable {

    //生成固定的序列化的版本Id
    private static final long serialVersionUID = -5794781839275410852L;
    String name ;
    transient int age ;  //transient 让这个字段(属性) 不参与序列化和反序列化

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

二、反射

2.1 反射的概念

 (Reflection),一个类在加载运行状态的时候,可以动态获取正在运行的类(Class)
 * 创建正在运行类的对象(构造器-Constructor)
 * 使用正在运行的这个类的对象去给 成员变量赋值(Field)
 * 调用正在运行的这个类中成员方法(Method)
 * 反射应用----Java设计模式
 * 正射 ----就是我们自己书写的代码的时候, 类名 对象名 =  new 类名() ;

2.2 面试题 获取一个类的字节码文件的方式?

1)调用Object类的getClass方法
2)任意Java类型的class属性
3)第三种方式:(推荐)
Java提供类Class:表示正在运行的Java应用程序中的类和接口
有一个静态方法:
参数:类的完全限定名称。 (包名.类名) String类型:经常用在配置文件中xx.properties
public static Class<?> forName(String className):

2.3 反射的基本使用(获取构造方法创建对象有参传值)

/**
 *  (Reflection),一个类在加载运行状态的时候,可以动态获取正在运行的类(Class)
 *  然后--->创建当前类对象
 *
 *  正常在写代码:
 *          Person p = new Person() ; //创建Person类的对象
 */
public class ReflectDemo2 {
    public static void main(String[] args)  throws Exception{
        System.out.println("正常的创建对象的格式:");
        Person p  = new Person() ;
        System.out.println(p);

        System.out.println("---------------------反射的方式:动态获取类的构造器并创建实例---------------------------------");
        //1)获取正在运行的类的字节码文件对象
        Class c = Class.forName("com.qf.reflect_02.Person");
        //2)public Constructor<?>[] getConstructors()throws SecurityException
        //获取正在运行的类中的所有公共的构造方法所在的Constructor类对象
        //public Constructor<?>[] getDeclaredConstructors():
        //获取正在运行的类中所有的构造方法所在的Constructor类对象
//        Constructor[] cons = c.getConstructors();
       /* Constructor[] cons = c.getDeclaredConstructors();
        for(Constructor con:cons){
            System.out.println(con);
        }*/

        //2)获取指定的构造器的Constructor对象,创建当前类实例
        //public Constructor<T> getConstructor(Class<?>... parameterTypes)
        //获取指定的公共的构造方法的Constructor对象,参数:指定参数类型的Class类对象,如果没有参数,可以不写
                        //举例:String类型的参数---java.lang.String
                        //int---->java.lang.Integer
        Constructor con = c.getConstructor();//无参构造器
        //System.out.println(con);//public com.qf.reflect_02.Person()
        //Constructor提供一个方法
        //参数:通过构造器里面给当前正在运行的类的成员变量赋值:实际参数
        //返回T---Type:代表任意Java对象
        //public T newInstance(Object... initargs):创建当前类实例
        Object obj = con.newInstance() ; // 多态 类似于 --->Object obj = new  Person() ;
        System.out.println(obj);
        //Person p2 = (Person) obj;
       // System.out.println(p2) ;


        System.out.println("----------------通过有参构造方法当前类实例---------------------------------");
        //Person person = new Person("高圆圆",44,"西安市") ;// 正常:私有不能直接创建

        //通过当前类的字节码文件对象获取指定的构造方法
        //参数是:当前参数类型的字节码文件 String--->String.class
        //public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        Constructor con2 = c.getDeclaredConstructor(String.class, int.class, String.class);//有参构造器
        //System.out.println(con2);

        //构造器所在Constructor继承AccessibleObject 提供
        // public void setAccessible(boolean flag) :参数为true:取消Java语言访问检查
        con2.setAccessible(true);
        //public T newInstance(Object... initargs):创建当前类实例
        Object obj2 = con2.newInstance("高圆圆", 44, "西安市南窑国际");
        System.out.println(obj2);


    }

    //public static void get(int...a){ //a代表多个参数:可变参数(当不明确具体有多个参数的时候,可以使用...)

    //}
}

2.4 访问成员变量(代码实现)

package com.qf.reflect_03;

import com.qf.reflect_02.Person;

import java.lang.reflect.Method;

/**
 * 动态获取指定类的字节码文件对象,创建当前类实例,调用里面的指定的成员方法!
 *
 * 之前写:
 *              Person p = new Person() ;
 *              p.show("javaee") ;
 */
public class ReflectDemo4 {

    public static void main(String[] args) throws Exception{
        System.out.println("-------------之前的写法-----------") ;
        Person p = new Person() ;
        p.show("helloworld") ;

        System.out.println("------------------反射的方式-------------------------");

        //1)获取指定类的字节码文件对象
        Class c = Class.forName("com.qf.reflect_02.Person") ;
        //2)无参构造方法公共的,Person类中,字节创建当前类实例
        Object obj = c.newInstance() ;
        //public Methods[] getMethods():获取这个类的所有的公共的成员方法的类对象Method(包括继承过来的以及实现接口的方法)
        //public Methods[] getDeclaredMethods():获取类所有的的指定的成员方法的类对象Method,不包括继承过来的

        //3)获取指定的公共成员方法
        //public Method getMethod(String name,Class<?>... parameterTypes)
        //参数1:方法的名称
        //参数2:当前方法中参数类型的Class
        Method m = c.getMethod("show", String.class);//参数2:java.lang.String
        //System.out.println(m);

        //4)调用方法
        //Method--->public Object invoke(Object obj,Object... args)
        //参数1:这个类的实例
        //参数2:方法中传入的实际参数
        m.invoke(obj, "helloworld");//本身这个方法没有返回值,可以不写;有返回值,使用Object类型接收

        System.out.println("-------------------------------------------------") ;
        //调用method方法
        //获取指定的成员方法所在的类对象Method
        //public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
        Method m2 = c.getDeclaredMethod("method", int.class) ;
        //System.out.println(m2) ;

        //取消Java语言检查
        m2.setAccessible(true) ;
        //调用方法
        Object returnObj = m2.invoke(obj, 100);
        System.out.println(returnObj);

        //其他方法调用


    }
}

 2.5 访问成员方法

package com.qf.reflect_03;

import com.qf.reflect_02.Person;

import java.lang.reflect.Method;

/**
 * 动态获取指定类的字节码文件对象,创建当前类实例,调用里面的指定的成员方法!
 *
 * 之前写:
 *              Person p = new Person() ;
 *              p.show("javaee") ;
 */
public class ReflectDemo4 {

    public static void main(String[] args) throws Exception{
        System.out.println("-------------之前的写法-----------") ;
        Person p = new Person() ;
        p.show("helloworld") ;

        System.out.println("------------------反射的方式-------------------------");

        //1)获取指定类的字节码文件对象
        Class c = Class.forName("com.qf.reflect_02.Person") ;
        //2)无参构造方法公共的,Person类中,字节创建当前类实例
        Object obj = c.newInstance() ;
        //public Methods[] getMethods():获取这个类的所有的公共的成员方法的类对象Method(包括继承过来的以及实现接口的方法)
        //public Methods[] getDeclaredMethods():获取类所有的的指定的成员方法的类对象Method,不包括继承过来的

        //3)获取指定的公共成员方法
        //public Method getMethod(String name,Class<?>... parameterTypes)
        //参数1:方法的名称
        //参数2:当前方法中参数类型的Class
        Method m = c.getMethod("show", String.class);//参数2:java.lang.String
        //System.out.println(m);

        //4)调用方法
        //Method--->public Object invoke(Object obj,Object... args)
        //参数1:这个类的实例
        //参数2:方法中传入的实际参数
        m.invoke(obj, "helloworld");//本身这个方法没有返回值,可以不写;有返回值,使用Object类型接收

        System.out.println("-------------------------------------------------") ;
        //调用method方法
        //获取指定的成员方法所在的类对象Method
        //public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
        Method m2 = c.getDeclaredMethod("method", int.class) ;
        //System.out.println(m2) ;

        //取消Java语言检查
        m2.setAccessible(true) ;
        //调用方法
        Object returnObj = m2.invoke(obj, 100);
        System.out.println(returnObj);

        //其他方法调用


    }
}

2.6 反射的应用

package com.qf.reflect_test_01;

import com.sun.corba.se.spi.orbutil.threadpool.Work;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 测试类
 *
 * 开发原则:所有开发原则必须"低耦合,高内聚"
 *      开闭原则:
 *              对现有代码的修改进行关闭,对扩展开放!
 */
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //使用学生的love方法
        /*Student s = new Student() ;
        s.love() ;*/

        //切换为工人类的love方法
        Worker worker = new Worker() ;
        worker.love();

        System.out.println("--------------------------------------------") ;
        //使用反射:--src下面提供xx.properties配置文件
        //创建一个属性集合列表---空的
        Properties prop = new Properties() ;
        //读取src下面的配置文件
        InputStream inputStream = ReflectTest.class.getClassLoader().
                getResourceAsStream("my.properties");

        //将配置文件中的内容 以"字节输入流"的方式加载到属性集合列表中
        prop.load(inputStream);
//        System.out.println(prop);

        //通过属性集合列表特有功能:getProperty(String key)---获取String value
        String className = prop.getProperty("className");
        //System.out.println(className) ;
        String methodName = prop.getProperty("methodName");
        //System.out.println(methodName);

        //通过反射获取当前类的字节码文件对象
        Class c = Class.forName(className) ;//class com.qf.reflect_test_01.Student
        //直接创建当前类实例
        Object obj   = c.newInstance();//等价于字节码文件对象获取无参构造方法的Constructor类对象--->Object newIntance()
        //System.out.println(obj);

        //获取当前类的成员方法的method类对象
        Method method = c.getDeclaredMethod(methodName);
        //调用方法
        method.invoke(obj) ;

    }
}
package com.qf.reflect_test_01;

/**
 * 学生类
 */
public class Student {
    public void love(){
        System.out.println("爱学习,爱Java!");
    }
}
package com.qf.reflect_test_01;

/**
 * 工人类
 */
public class Worker {
    public void love(){
        System.out.println("爱生活,爱高圆圆!");
    }
}

   

my.properties
className=com.qf.reflect_test_01.Worker
methodName=love

2.7 反射中的常见面试题

1)有一个集合ArrayList<String>,存储了一些字符串数据了,如何给里面添加Integer类型的数据?
	add(E e)--->只能通过反射获取到add方法---所在的Method类对象
2)利用反射解决代码重复修改---->将一些内容存储到配置文件中	

public class ReflectTest {
    public static void main(String[] args) throws Exception {
        ArrayList <String> list = new ArrayList<>();
        //正常只能添加String类型的数据
        list.add("hello");
        list.add("100");
        //获取ArrayList的字节码文件名称
        System.out.println(ArrayList.class);
        //获取ArrayList的字节码文件的对象
        Class<?> c = Class.forName("java.util.ArrayList");
        //获取指定成员方法
        Method add = c.getDeclaredMethod("add",Object.class);
        //调用方法
        add.invoke(list,108);
        System.out.println(list);


    }
}

2.8 反射的jdk动态代理(理解并且掌握)

 *   代理设计模式---属于"结构型设计模式"
 *   代理核心思想:代理角色帮助真实角色对他的业务功能进行增强!
 *              静态代理
 *                      特点:代理角色和真实角色必须实现同一个接口!
 *                      弊端:业务功能和 增强的功能没有完全分离!
 *              动态代理
 *                      jdk动态代理--->jdk提供的
 *                                      java.lang.reflect.Proxy:动态代理类
 *                           静态方法:
 *                                  public static Object newProxyInstance(
 *                                        ClassLoader loader, //获取接口实例的类加载器
 *                                       Class<?>[] interfaces,//实现的接口列表的字节码文件(代理类要的实现接口列表的Class)
 *                                       InvocationHandler h)//有代理实例调用 接口方法的处理程序:接口类型 throws IllegalArgumentException
 *                 InvocationHandler:接口
 *                public Object invoke(Object proxy, Method method, Object[] args)
 *                          参数1:调用该方法的代理实例
 *                          参数2:调用该代理实例的接口方法的Method类对象
 *                          参数3:调用接口方法中所有的参数,组成数组,没有参数,可不写
 *
 *                      cglib动态代理----不需要接口,基于类就可以实现动态代理
 *                      必须提供第三方jar包  (cglib-3.3.0.jar)
 *
 */
package com.qf_reflect_02;

/**
 * 针对用户的数据访问接口
 *  这个接口有针对用户的添加,删除,修改,查询的功能
 */
public interface UserDao {

    /**
     * 添加用户操作
     */
    void add() ;

    /**
     * 修改用户
     */
    void update() ;

    /**
     * 删除用户
     */
    void delete() ;

    /**
     * 查询所有用户
     */
    void findAll() ;
}
package com.qf_reflect_02;

/**
 * 针对用户的数据接口实现
 */
public class UserDaoImpl  implements UserDao{
    /**
     * 添加用户操作
     */
    @Override
    public void add() {
        System.out.println("添加用户操作") ;
    }

    /**
     * 修改用户
     */
    @Override
    public void update() {
        System.out.println("修改用户") ;
    }

    /**
     * 删除用户
     */
    @Override
    public void delete() {
        System.out.println("删除用户") ;
    }

    /**
     * 查询所用
     */
    @Override
    public void findAll() {
        System.out.println("查询所有用户") ;
    }
}

package com.qf_reflect_02;

/**
 * 优化之后的实现类:对add/delete/udate/findAll这些功能进行增强!
 */
public class UserDaoImpl2  implements UserDao{
    private  UserDao ud ;
    public UserDaoImpl2(UserDao ud){
        this.ud = ud ;
    }
    @Override
    public void add() {
        System.out.println("权限校验") ; //---->第三方提供的: 系统监控代码 shiro框架:权限框架/SpringSecurity权限框架
        //自己的业务功能
        ud.add() ;
        System.out.println("产生日志记录");
    }

    @Override
    public void update() {
        System.out.println("权限校验" ) ;
        //自己的业务功能
        ud.update(); ;
        System.out.println("产生日志记录");
    }

    @Override
    public void delete() {
        System.out.println("权限校验" ) ;
        //自己的业务功能
        ud.delete(); ;
        System.out.println("产生日志记录");
    }

    @Override
    public void findAll() {
        System.out.println("权限校验" ) ;
        //自己的业务功能
        ud.findAll(); ;
        System.out.println("产生日志记录");
    }
}
package com.jdkproxy_03;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 自定义一个类实现InvocationHandler接口
 * 实现invoke方法---->就是调用接口方法
 *
 * 代理实例要调用接口方法的处理程序
 */
public class MyInvocationHandler implements InvocationHandler {

    //声明Object变量
    private  Object  target ;
    public MyInvocationHandler(Object target){// 传入真实角色
        this.target =  target ;
    }

    /**
     *
     *
     *
     *
     * @param proxy  调用该方法的代理实例
     * @param method 调用该代理实例的接口方法的Method类对象
     * @param args 调用接口方法中所有的参数,组成数组,没有参数,可不写
     * @return 从代理实例上的方法调用返回的值
     * @throws Throwable 可能调用方法失败!
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("操作业务功能之前,先权限校验!" );

        //反射的写法调用接口方法--add()/delete()/udate()/findAll()
        Object obj = method.invoke(target, args);

        System.out.println("操作业务功能之后,产生日志记录!");
        return obj; //方法的返回值
    }
}


package com.qf_reflect_02;

import com.jdkproxy_03.MyInvocationHandler;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * 测试类
 * 提供好了一个接口,---对用户的添加删除修改查询的功能
 * 在具体的实现类中进行实现类;
 * 需求:
 *      想对添加/删除/修改/查询的功能之前,完成一些事情 "权限校验"功能,操作这些业务方法之后,产生"日志记录"这些事情!
 *
 *
 */
public class UserTest {
    public static void main(String[] args) {
        //创建UserDao接口对象---子实现类创建
        UserDao ud = new UserDaoImpl() ; //ud:真实角色
        ud.add() ;
        ud.update() ;
        ud.delete() ;
        ud.findAll();
        System.out.println("------------------优化后:对业务功能增强(静态代理)-------------------") ;
        UserDaoImpl2 ud2 = new UserDaoImpl2(ud) ; //代理角色
        ud2.add() ;
        ud2.delete();
        ud2.update();
        ud2.findAll();

        System.out.println("---------------------JDK动态代理实现--------------------------------------------") ;

        //接口多态
        InvocationHandler handler = new MyInvocationHandler(ud) ;
        UserDao ud3 = (UserDao) Proxy.newProxyInstance(
                ud.getClass().getClassLoader(),//代理实例要实现的接口的字节码文件---类加载
                //代理实例要实现接口列表的Class--->Class类中--public 类<?>[] getInterfaces()
                ud.getClass().getInterfaces(),
                //代理实例调用接口方法的处理程序
                handler);

        ud3.add() ;
        ud3.delete() ;
        ud3.update() ;
        ud3.findAll() ;
    }
}

三、网络聊天室(第一版)

publicclassClientTcp01{
publicstaticvoidmain(String[]args){
Sockets=null;
try{
//创建Socket对象
s=newSocket("127.0.0.1",10089);
//获取客户端通道的输出和输入流
InputStreamin=s.getInputStream();
OutputStreamout=s.getOutputStream();

//创建键盘录入对象
Scannersc=newScanner(System.in);

//客户端不断的发送消息
while(true){
System.out.println("请输入消息:");
Stringmsg=sc.nextLine();
//使用输出流发送过去
out.write(msg.getBytes());

//客户端读取服务器发送的消息
byte[]bys=newbyte[1024];
intlen=in.read(bys);
StringmsgStr=newString(bys,0,len);
System.out.println("客户端收到服务器发送的消息:"+msgStr);
}
}catch(IOExceptione){
e.printStackTrace();
}finally{
if(s!=null){
try{
s.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
}
}
publicclassServerTcp{
publicstaticvoidmain(String[]args){
ServerSocketss=null;
try{
//创建服务器端Socket对象
ss=newServerSocket(10089);
//监听客户端
Sockets=ss.accept();
//获取服务器端通道的输出和输入流
InputStreamin=s.getInputStream();
OutputStreamout=s.getOutputStream();
//创建键盘录入对象
Scannersc=newScanner(System.in);
//服务器端不断的读取消息和回复消息
while(true){
//读消息
byte[]bys=newbyte[1024];
intlen=in.read(bys);
Stringmsg=newString(bys,0,len);
//回复消息
System.out.println("服务器端收到消息"+msg+"并回复消息:");
StringmsgStr=sc.nextLine();
out.write(msgStr.getBytes());
}

}catch(IOExceptione){
e.printStackTrace();
}finally{
if(ss!=null){
try{
ss.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
}
}

第二版(网聊天优化使用集合)线程加集合

package com.qf.chatroom.client;

import com.qf.chatroom.thread.ClientReadThread;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * 网络聊天:
 *      TCP方式---保证安全(可靠连接,建立连接通道)
 *     客户端不断的发送消息,不断的去读取服务器端反馈的消息!
 */
public class Client {

    public static void main(String[] args) {
        try {
            //创建客户端的Socket对象
            Socket socket  = new Socket("10.35.165.17",8888) ;
            //获取客户端所在的通道内的字节输入和字节输出流
            OutputStream out = socket.getOutputStream() ;
            InputStream in = socket.getInputStream();

            //创建键盘录入对象
            Scanner sc = new Scanner(System.in) ;

            //客户端开启读取服务器回复的消息的子线程
            ClientReadThread crt = new ClientReadThread(socket)  ;
            crt.start() ;

            //不断的去发送消息
            while (true){
                System.out.println("请发送消息:" );
                String message = sc.nextLine() ;
                //约定客户端发送消息的格式: "接收者:消息内容:发送者"  (:英文符号)

                out.write(message.getBytes()) ;

                //读取服务器反馈的消息
                /*byte[] bytes = new byte[1024] ;
                int len = in.read(bytes);
                String fkMessage = new String(bytes,0,len) ;
                //展示服务器反馈的消息
                System.out.println(fkMessage);*/
            }



            /**
             * 在用户线程main中,客户端和服务器端都一样,既去写消息,又要读消息,
             * read方法本身就是阻塞式方法,如果服务器端监听多个客户端,多个客户端同时去给服务器端消息,可能消息阻塞现象!
             * 不安全!--------->开启多线程的方式
             *          一般子线程中:"读消息",不会录入的消息 (main用户线程中录入)
             *          客户端和服务器端---分别开启读消息的子线程-----使用 继承Thread类实现
             */


        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
//客户端线程
package com.qf.chatroom.thread;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

/**
 * 客户端读取服务器发送过来的消息的子线程
 */
public class ClientReadThread extends Thread {
    private Socket s ;

    public ClientReadThread(Socket s) {
        this.s = s ;
    }

    @Override
    public void run() {
        //不断的去读
        try {
            while(true){
                //获取客户端通道内的字节输入流
                InputStream in = s.getInputStream();

                byte[] bytes = new byte[1024] ;
                int len = in.read(bytes);
                String fkMessage = new String(bytes,0,len) ;
                //展示服务器反馈的消息
                System.out.println(fkMessage);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(s!=null){
                try {
                    s.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }


    }
}
服务器端:
package com.qf.chatroom.server;

import com.qf.chatroom.thread.ServerReadThread;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;

/**
 * 服务器端:
 *      不断读取客户端写过来的消息,给客户端不断回复数据
 *
 *      真实场景:
 *              服务器端不回复消息,作用转发消息,一般客户端和客户端聊天!
 *              张三客户端---->服务器端----->消息转发----->李四客户端
 */
public class Server {
    public static void main(String[] args) {

        //创建服务器端的Socket
        try {
            ServerSocket ss = new ServerSocket(8888) ;
            //创建List集合--默认ArrayList,存储每一个客户端!
            ArrayList<Socket> list = new ArrayList<>() ;
            System.out.println("服务器正在等待连接...");
            int i = 1 ;

            while(true){

                //监听客户端连接
                Socket socket = ss.accept() ;
                //不断的监听客户端连接
                System.out.println("第"+(i++)+"个客户端已连接...");

                //获取到的客户端通道内的字节输入和字节输出流
                InputStream in = socket.getInputStream();
                OutputStream out = socket.getOutputStream() ;
                //将每一个客户端所在Socket对象存储在List集合中
                list.add(socket) ;
                System.out.println(list) ;

                //开启服务器端读取客户端消息的子线程
                ServerReadThread srt = new ServerReadThread(socket,list) ;
                srt.start();
            }

            //创建键盘录入对象
           // Scanner sc = new Scanner(System.in) ;



            //不断去读取消息和回复消息
            /*while(true){
                //不断的回消息
                System.out.println("请回复消息: ") ;
                String fkMessage = sc.nextLine() ;

                out.write(fkMessage.getBytes());

                //服务器不关闭
            }*/

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
服务器线程端:
package com.qf.chatroom.thread;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;

/**
 * 服务器端读取客户端发送过来的消息的子线程
 */
public class ServerReadThread  extends  Thread{

    private Socket s  ;
    private ArrayList<Socket> list ;
    public ServerReadThread(Socket s,ArrayList<Socket> list) {
        this.s = s ;
        this.list = list ;
    }

    @Override
    public void run() {
        try {
            while(true){
                //获取到监听客户端所在的通道内的字节输入流
                InputStream in = s.getInputStream();

                //不断去读取客户端发送过来的消息
                byte[] bytes = new byte[1024] ;
                int len = in.read(bytes);
                //展示客户端发送的消息以及ip
                String ip = s.getInetAddress().getHostAddress() ;
                String receiveMessage = new String(bytes,0,len) ;
                System.out.println("消息是:"+receiveMessage) ;

                //receiveMessage "接收者:消息内容:发送者"(客户端发送端 消息格式)
                //举例:两个客户端:  0   1
                //"1:你好:0"
                //通过":"进行字符串拆分
                String[] strs = receiveMessage.split(":") ;
                //strs[0] :接收者
                String receiver = strs[0] ;
                //strs[1] :消息内容
                String messageContent = strs[1] ;
                //strs[2]:发送者
                String sender = strs[2] ;

                //获取接收者所在的通道内的Socket对象   ArrayList<Socket>通过的角标获取Socket对象
                Socket receiverSocket = list.get(Integer.parseInt(receiver)) ;
                //获取接收者所在通道内的字节输出流
                OutputStream out = receiverSocket.getOutputStream();
                //服务器端组织给客户端的消息--->写给接收者
                out.write((sender+"对你说"+messageContent).getBytes()) ;

                //服务器转发消息
                //不展示数据
                //System.out.println(ip+"---"+receiveMessage) ;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //服务器端不关闭

    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值