网络编程

昨天讲解了网络编程的理论部分,今天主要围绕网络编程的开发,进行技术的梳理

主要技术点:Socket编程+IO流+多线程

一、基本互发数据

案例:客户端发数据给服务器,服务器也可以给客户端发数据

//创建客户端socket对象,指定服务器的ip和端口
Socket socket = new Socket("127.0.0.1", 6666);

//客户端发数据
OutputStream os = socket.getOutputStream();
os.write("client说:xxxxxxx".getBytes());

//客户端接收数据
InputStream is = socket.getInputStream();
byte[] b = new byte[1024];
int len = is.read(b);   //阻塞
System.out.println(new String(b,0,len));

Utils.closeAll(os,is,socket);
//1.创建服务器socket,指定自身端口
ServerSocket ss = new ServerSocket(6666);
Socket socket = ss.accept(); //阻塞

//服务器接收:
InputStream is = socket.getInputStream();
byte[] b = new byte[1024];
int len = is.read(b); //阻塞
System.out.println(new String(b,0,len));

//服务器发数据:
OutputStream os = socket.getOutputStream();
os.write("server说:yyyyyy".getBytes());

Utils.closeAll(os,is,socket);  //统一关闭

二、上传图片

案例:从客户端上传图片资源到服务器

分析

传输二进制资源可以使用字节节点流即可

通过画图分析可知:

  1. 客户端需要从本地读资源,写到socket中–循环的先读后写
  2. 服务器从socket读,再写到服务器本地—–循环的先读后写

上传实现

//客户端:
Socket socket = new Socket("127.0.0.1", 7777);

//客户端从本地读图片资源,再写到socket
InputStream is = new FileInputStream("c.jpg");
int len;
byte[] b = new byte[1024];
OutputStream os = socket.getOutputStream();
while((len=is.read(b))!=-1) { //循环先读后写
    os.write(b, 0, len);
}
System.out.println("上传成功!");
Utils.closeAll(is,os,socket);
//服务器:
ServerSocket ss = new ServerSocket(7777);
Socket socket = ss.accept();

InputStream is = socket.getInputStream();  //从socket中读取资源
int len;
byte[] b = new byte[1024];
OutputStream os = new FileOutputStream("d:/c.jpg");
while((len=is.read(b))!=-1) { //循环的先读后写;写到了服务器本地
    os.write(b, 0, len);
}
System.out.println("存到服务器成功~");
Utils.closeAll(is,os,socket);

三、多客户端发数据

案例:多个客户端启动后,可以循环发数据到服务器端

分析

每个客户端都应该对应一个服务器的socket;来一个客户端则产生一个服务器socket;意味着服务器也需要循环监听,且需要通过多线程分离一条路径与客户都交互。

多个客户端循环的从控制台录入数据;服务器都需要能够接收到

优化:将字节流封装成字符缓冲流

技术点: socket+IO流+多线程

代码实现

//客户端:
//创建一个客户端类,只需要运行多次,则是模拟多个客户端
Socket socket = new Socket("127.0.0.1", 5555);
System.out.println("客户端输入数据:");
Scanner sc = new Scanner(System.in);
OutputStream os = socket.getOutputStream(); //socket的字节流
//封装成字符缓冲流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
while(true) {
    String msg = sc.next();
    bw.write(msg);  //写数据
    bw.newLine();   //换行
    bw.flush();     //刷新缓冲
    if(msg.equals("886")) { //退出出口
        break;
    }
}
Utils.closeAll(bw,os,socket);
//服务器:
//服务器循环监听客户端的访问
ServerSocket ss = new ServerSocket(5555);
Socket socket = null;
while(true) { //循环监视客户端访问
    socket = ss.accept();  //每次客户端来了,产生子线程;主线从都会继续阻塞
    new ServerThread(socket).start(); //启动线程和客户端的socket交互
}
//服务器线程:
public class ServerThread extends Thread {
    private Socket socket;
    public ServerThread(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        InputStream is = null;
        BufferedReader br = null;
        try {
            is = socket.getInputStream();  //获取字节流
            br = new BufferedReader(new InputStreamReader(is));
            while(true) {
                String msg = br.readLine();
                System.out.println(msg);
                if(msg.equals("886")) {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            Utils.closeAll(br,is,socket);
        } 
    }
}

四、注册功能

案例分析

在web开发中,注册的流程:前端准备注册数据,传到后端;后端接收数据,调取数据库的数据,匹配用户信息,如果用户名相同,返回已注册;没有相同的,则进行存储到数据库,并返回注册成功。

因为现在没有数据库基础,没有web服务器技术;所以只能通过tcp编程的方式,模拟注册功能。

客户端–前端页面 服务器端—web后端 数据库—-文本文件(Properties)

Properties存储格式:key-ID,value-所有数据拼接

数据处理:从客户端发数据,服务器接收;再通过字符串分解匹配;并存储到文件;服务器反馈信息

代码实现

//客户端:
public static void main(String[] args) throws IOException {
    Socket socket = new Socket("127.0.0.1", 8888);
    OutputStream os = socket.getOutputStream();
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
    String msg = getInfo();  //封装数据
    bw.write(msg);  //"id:1001,name:zs,pwd:123,age:30"
    bw.newLine();
    bw.flush();
    InputStream is = socket.getInputStream();
    byte[] b = new byte[1024];
    int len = is.read(b);
    System.out.println(new String(b,0,len));
    Utils.closeAll(os,is,bw,socket);
}
private static String getInfo() {
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入你要注册的信息:");
    System.out.print("ID:");
    String id = sc.next();
    System.out.print("name:");
    String name = sc.next();
    System.out.print("pwd:");
    String pwd = sc.next();
    System.out.print("age:");
    String age = sc.next();
    return "id:"+id+",name:"+name+",pwd:"+pwd+",age:"+age;
}
//服务器:
ServerSocket ss = new ServerSocket(8888);
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//接收数据
String msg = br.readLine(); //"id:1001,name:zs,pwd:123,age:30"
//拆分字符串: 
String ids = msg.split(",")[0]; //id:1001
String id = ids.split(":")[1];
Properties p = new Properties(); //实例化集合,并加载配置文件的数据
p.load(new FileInputStream("user.properties")); //将文件数据读到p集合

OutputStream os = socket.getOutputStream();
if(p.containsKey(id)) {
    os.write("已注册".getBytes());
}else {
    os.write("注册成功~".getBytes());
    p.put(id, msg);
    p.store(new FileOutputStream("user.properties"), "");
}
Utils.closeAll(is,os,br,socket);

五、登录功能

案例分析

客户端:准备登录的id和pwd,从客户端发送到服务器

服务器:接收到字符串,拿到ID和密码;匹配配置文件中的key;及匹配value的密码

id和密码都一致,则返回登录成功;否则登录失败

代码实现

//客户端:
public static void main(String[] args) throws UnknownHostException, IOException {
    Socket socket = new Socket("127.0.0.1", 4444);
    OutputStream os = socket.getOutputStream();
    String msg = getInfo(); //id:1001,pwd:123
    os.write(msg.getBytes());  //发送数据

    InputStream is = socket.getInputStream();  //接收服务器的返回信息
    byte[] b = new byte[1024];
    int len = is.read(b);
    System.out.println(new String(b,0,len));
    Utils.closeAll(is,os,socket);  //统一关闭
}

private static String getInfo() {
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入要登录的信息:");
    System.out.print("id:");
    String id = sc.next();
    System.out.print("pwd:");
    String pwd = sc.next();
    return "id:"+id+",pwd:"+pwd;
}
//服务器:
ServerSocket ss = new ServerSocket(4444);
Socket socket = ss.accept();
InputStream is = socket.getInputStream();  //socket中获取输入流
byte[] b = new byte[1024];
int len = is.read(b);
String msg = new String(b,0,len); //id:1001,pwd:123
String ids = msg.split(",")[0]; //id:1001
String pwd = msg.split(",")[1]; //pwd:123
String id  = ids.split(":")[1]; //1001
Properties p = new Properties(); 
p.load(new FileInputStream("user.properties"));//加载配置文件数据到集合
String value = p.getProperty(id); //根据key获取value

OutputStream os = socket.getOutputStream();
if(value!=null&&value.contains(pwd)) {//判断id和pwd相等
    os.write("登录成功".getBytes());
}else {
    os.write("登录失败".getBytes());
}
Utils.closeAll(os,is,socket);

六、反射概述

概述:反射其实就是类对象,类加载的产物;将类加载到内存中,会产生类对象(class文件);有了类对象,即可得到所有类资源的信息: 类,方法,属性,构造器,包,父类,接口…

创建类对象

可以通过三种方式获取类对象:1.类名.class 2.对象.getClass() 3.Class.forName(“全限定名”);

结论:无论哪种方式获取的类对象都是同一个类对象

//1.通过类名获取类对象
Class<?> c1 = Person.class;  //<?>:通配所有类型,用在接收数据据时

Class c2 = new Person().getClass();

Class c3 = Class.forName("com.qf.f_reflect.Person");

System.out.println(c1==c2); //true
System.out.println(c1==c3); //true

常用方法

//反射的相关方法
Class c1 = Student.class;  //获取反射对象
System.out.println("获取类名:"+c1.getName());
System.out.println("获取包名:"+c1.getPackage());
System.out.println("获取父类:"+c1.getSuperclass());
System.out.println("获取接口:"+Arrays.toString(c1.getInterfaces()));

//getField或getFields获取公开的属性的
System.out.println("获取属性:"+Arrays.toString(c1.getFields()));
//System.out.println("获取指定属性:"+c1.getField("name"));

//获取非空开属性;甚至放开权限后,私有的也可以获取
System.out.println("获取属性2:"+Arrays.toString(c1.getDeclaredFields())); //常用

System.out.println("获取方法:"+Arrays.toString(c1.getMethods())); //常用
//参数1:方法名  参数2:多个参数
System.out.println("获取指定方法:"+c1.getMethod("hi",String.class,int.class));
System.out.println("获取构造器:"+Arrays.toString(c1.getConstructors()));
System.out.println("实例化对象:"+c1.newInstance()); //(常用)

七、总结与作业

总结

作业

1.使用socket完成,客户端发送信息,服务器接收信息(使用字节流收发数据)
2.使用socket完成,客户端发送信息,服务器接收信息(使用字符缓冲流收发数据)
3.实现客户端从服务器中下载图片功能(类似上传图片)
提示:服务器发(服务器本地读;再发到socket)---->客户端收(从socket读取,再存到客户端本地)

晨考

1.TCP是一种面向_连接____,_可靠的____,基于_字节流___的传输通讯协议
 
  UDP是一种_无连接的____,_不可靠的__的传输通讯协议
2.什么是IP
ip是计算机的唯一识别地址
3.ServerSocket的accept方法会导致程序_阻塞______,直到接收到_客户端连接______,才继续运行
4. 客户端与服务器建立连接的语句,请默写   
Server
ServerSocket ss  = new ServerSocket(xxxx);
Socket sk = ss.accept();

Client
Socket st = new Socket("localhost",xxxxx);