传智播客-Java学习笔记day26

1.网络模型概述和图解
l网络模型一般是指

  OSI(Open System Interconnection开放系统互连)参考模型

  TCP/IP参考模型

 

网络模型7层概述:

1.物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。

2. 数据链路层:主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。

3. 网络层:主要将从下层接收到的数据进行IP地址(例192.168.0.1)的封装与解封装。在这一层工作的设备是路由器,常把这一层的数据叫做数据包。

4. 传输层:定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。 主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层数据叫做段。

5.会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)

6.表示层:主要是进行对接收的数据进行解释、加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够能识别的东西(如图片、声音等)。

7.应用层: 主要是一些终端的应用,比如说FTP(各种文件下载),WEB(IE浏览),QQ之类的(可以把它理解成我们在电脑屏幕上可以看到的东西.就是终端应用)。

 

2.网络编程三要素-----IP地址

 

网络编程三要素:
    A:IP地址
    B:端口
    C:协议
 

我们要进行通讯,需要哪些要素呢?

比如说:我要跟你说话.

第一个条件:我要先找到你 (IP)

第二个条件:你得有接收数据的地方  耳朵 (端口)

第三个条件:我跟你说话,你能接收到,咱按什么方式接收啊,我说英文你懂吗,说韩文你懂吗,不懂是吧,所以我还是说中文把.(协议)

 
举例:
    我想和林青霞说话了。肿么办?
    A:我要找到林青霞。
    B:对她说话,要对耳朵说。
    C:我说什么呢?"I Love You"
      但是,她没学过英语,听不懂。
      我没必要说英语,说汉语就可以了:我爱你
 
IP地址:
    网络中计算机的唯一标识。
 
    计算机只能识别二进制的数据,所以我们的IP地址应该是一个二进制的数据。
    但是呢,我们配置的IP地址确不是二进制的,为什么呢?
        IP:192.168.1.100
        换算:11000000 10101000 00000001 01100100
    假如真是:11000000 10101000 00000001 01100100的话。
    我们如果每次再上课的时候要配置该IP地址,记忆起来就比较的麻烦。
    所以,为了方便表示IP地址,我们就把IP地址的每一个字节上的数据换算成十进制,然后用.分开来表示:
        "点分十进制"
 
    IP地址的组成:网络号段+主机号段
        A类:第一号段为网络号段+后三段的主机号段
            一个网络号:256*256*256 = 16777216
        B类:前二号段为网络号段+后二段的主机号段
            一个网络号:256*256 = 65536
        C类:前三号段为网络号段+后一段的主机号段
            一个网络号:256
 
    IP地址的分类:
        A类    1.0.0.1---127.255.255.254    (1)10.X.X.X是私有地址(私有地址就是在互联网上不使用,而被用在局域网络中的地址)                            (2)127.X.X.X是保留地址,用做循环测试用的。
        B类    128.0.0.1---191.255.255.254    172.16.0.0---172.31.255.255是私有地址。169.254.X.X是保留地址。
        C类    192.0.0.1---223.255.255.254    192.168.X.X是私有地址
        D类    224.0.0.1---239.255.255.254     
        E类    240.0.0.1---247.255.255.254
 
    两个DOS命令:
        ipconfig 查看本机ip地址
        ping 后面跟ip地址。测试本机与指定的ip地址间的通信是否有问题
 
    特殊的IP地址:
        127.0.0.1 回环地址(表示本机)//也就是说,ping本机的IP地址相当于ping 127.0.0.1
        x.x.x.255 广播地址
        x.x.x.0 网络地址
 
3.InetAddress类的概述和使用
如何获取和操作IP地址呢?

  为了方便我们对IP地址的获取和操作,java提供了一个类InetAddress供我们使用。

 

/*
 * 如果一个类没有构造方法
 * A:成员全部是静态的(Math,Arrays,Collections)
 * B:单例设计模式(Runtime)
 * C:类中有静态方法返回该类的对象(InetAddress)
 *         class Demo {
 *             private Demo(){}
 * 
 *             public static Demo getXxx() {
 *                 return new Demo();
 *             }
 *         }
 * 
 * 看InetAddress的成员方法:
 * public static InetAddress getByName(String host):根据主机名或者IP地址的字符串表示得到IP地址对象
 */
public class InetAddressDemo {
    public static void main(String[] args) throws UnknownHostException {
        // public static InetAddress getByName(String host)
        // InetAddress address = InetAddress.getByName("liuyi");
        // InetAddress address = InetAddress.getByName("192.168.12.92");
        InetAddress address = InetAddress.getByName("192.168.12.63");
 
        // 获取两个东西:主机名,IP地址
        // public String getHostName()
        String name = address.getHostName();
        // public String getHostAddress()
        String ip = address.getHostAddress();
        System.out.println(name + "---" + ip);
    }
}
 
=====================================
一个奇怪的小现象
 
以太网适配器 本地连接:
 
   连接特定的 DNS 后缀 . . . . . . . :
   IPv6 地址 . . . . . . . . . . . . : 2001:250:3c00:2335:c4a5:1d7f:b668:
   临时 IPv6 地址. . . . . . . . . . : 2001:250:3c00:2335:ddad:5f45:3400:
   本地链接 IPv6 地址. . . . . . . . : fe80::c4a5:1d7f:b668:f26c%12
   IPv4 地址 . . . . . . . . . . . . : 172.29.111.93
   子网掩码  . . . . . . . . . . . . : 255.255.255.0
   默认网关. . . . . . . . . . . . . : fe80::72f9:6dff:fe70:348f%12
                                       172.29.111.254
 
以太网适配器 VirtualBox Host-Only Network:
 
   连接特定的 DNS 后缀 . . . . . . . :
   本地链接 IPv6 地址. . . . . . . . : fe80::d506:ceb4:be6:69b5%51
   IPv4 地址 . . . . . . . . . . . . : 192.168.56.1
   子网掩码  . . . . . . . . . . . . : 255.255.255.0
   默认网关. . . . . . . . . . . . . :
 
 
4.网络编程三要素之端口,协议
 
端口号:
    正在运行的程序的标识。
    有效端口:0~65535,其中0~1024系统使用或保留端口。

 

协议:
    通信的规则
 
    UDP:
        把数据打包
        数据有限制
        不建立连接
        速度快
        不可靠
 
    TCP:
        建立连接通道
        数据无限制
        速度慢
        可靠
 
    举例:
        UDP:发短信
        TCP:打电话
 
5.Socket通信原理图解
 
Socket包含了:IP地址+端口
6.UDP协议发送数据
UDP协议发送数据:
  A:创建发送端Socket对象
  B:创建数据,并把数据打包
  C:调用Socket对象的发送方法发送数据包
  D:释放资源
要注意的是Socket对象在UDP中指的不是Socket类,而是DatagramSocket,Socket是TCP用的
public static void main(String[] args) throws IOException {
        // 创建发送端Socket对象
        // DatagramSocket()
        DatagramSocket ds = new DatagramSocket();
 
        // 创建数据,并把数据打包
        // DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        // 创建数据
        byte[] bys = "hello,udp,我来了".getBytes();//巧妙把字符串转换为字节数组
        // 长度
        int length = bys.length;
        // IP地址对象
        InetAddress address = InetAddress.getByName("192.168.12.92");
        // 端口
        int port = 10086;//端口号的选择:一万号开外
        DatagramPacket dp = new DatagramPacket(bys, length, address, port);
 
        // 调用Socket对象的发送方法发送数据包
        // public void send(DatagramPacket p)
        ds.send(dp);
 
        // 释放资源
        ds.close();//底层依赖IO流,所以要释放资源
    }
======================================================
 
7.UDP协议接收数据
 
  UDP协议接收数据:
  A:创建接收端Socket对象
  B:创建一个数据包(接收容器)
  C:调用Socket对象的接收方法接收数据
  D:解析数据包,并显示在控制台
  E:释放资源
 
public static void main(String[] args) throws IOException {
        // 创建接收端Socket对象
        // DatagramSocket(int port)
        DatagramSocket ds = new DatagramSocket(10086);//端口号的选择:一万号开外
 
        // 创建一个数据包(接收容器)
        // DatagramPacket(byte[] buf, int length)
        byte[] bys = new byte[1024];
        int length = bys.length;
        DatagramPacket dp = new DatagramPacket(bys, length);
 
        // 调用Socket对象的接收方法接收数据
        // public void receive(DatagramPacket p)
        ds.receive(dp); // 阻塞式(在没有接收到数据之前等待)
 
        // 解析数据包,并显示在控制台
        // 获取对方的ip
        // public InetAddress getAddress()
        InetAddress address = dp.getAddress();
        String ip = address.getHostAddress();
        // public byte[] getData():获取数据缓冲区
        // public int getLength():获取数据的实际长度
        byte[] bys2 = dp.getData();
        int len = dp.getLength();
        String s = new String(bys2, 0, len);
        System.out.println(ip + "传递的数据是:" + s);
 
        // 释放资源
        ds.close();
    }
小的注意:
===================================================
UDP协议发送和接收数据图解
8.UDP协议发送和接收数据代码优化)
利用链式编程进行优化
先写ReceiveDemo----接收端
public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
        // 创建接收端的Socket对象
        DatagramSocket ds = new DatagramSocket(12345);
 
        // 创建一个包裹
        byte[] bys = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bys, bys.length);
 
        // 接收数据
        ds.receive(dp);
 
        // 解析数据
        String ip = dp.getAddress().getHostAddress();
        String s = new String(dp.getData(), 0, dp.getLength());
        System.out.println("from " + ip + " data is : " + s);
 
        // 释放资源
        ds.close();
    }
}
 
一个注意的问题:接收端不可以连续运行两次
 
  多次启动接收端:
          java.net.BindException: Address already in use: Cannot bind
          端口被占用
 
==============================================================
再写发送端
public class SendDemo {
    public static void main(String[] args) throws IOException {
        // 创建发送端的Socket对象
        DatagramSocket ds = new DatagramSocket();
 
        // 创建数据并打包
        byte[] bys = "helloworld".getBytes();
        DatagramPacket dp = new DatagramPacket(bys, bys.length,
                InetAddress.getByName("192.168.12.92"), 12345);
 
        // 发送数据
        ds.send(dp);
 
        // 释放资源
        ds.close();
    }
}
 
 
====================================================
几个注意问题
 
 
9.DOS窗口演示简易聊天小程序
首先把上一次的那两个程序单独拿出来放到同一个文件夹中、
 
 
然后,编译运行
10.多线程实现聊天室程序
首先新建一个ChatRoom类
/*
 * 通过多线程改进刚才的聊天程序,这样我就可以实现在一个窗口发送和接收数据了
 */
public class ChatRoom {
    public static void main(String[] args) throws IOException {
        DatagramSocket dsSend = new DatagramSocket();
        DatagramSocket dsReceive = new DatagramSocket(12306);
 
        SendThread st = new SendThread(dsSend);
        ReceiveThread rt = new ReceiveThread(dsReceive);
 
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(rt);
 
        t1.start();
        t2.start();
    }
}
================================================================
然后慢慢填坑
SendThread 类
public class SendThread implements Runnable {
 
    private DatagramSocket ds;
 
    public SendThread(DatagramSocket ds) {//通过带参构造方法传参数
        this.ds = ds;
    }
 
    @Override
    public void run() {
        try {
            // 封装键盘录入数据
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    System.in));
            String line = null;
            while ((line = br.readLine()) != null) {
                if ("886".equals(line)) {
                    break;
                }
 
                // 创建数据并打包
                byte[] bys = line.getBytes();
                // DatagramPacket dp = new DatagramPacket(bys, bys.length,
                // InetAddress.getByName("192.168.12.92"), 12345);
                DatagramPacket dp = new DatagramPacket(bys, bys.length,
                        InetAddress.getByName("192.168.12.255"), 12306);
 
                // 发送数据
                ds.send(dp);
            }
 
            // 释放资源
            ds.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
}
=================================================
ReceiveThread 类
public class ReceiveThread implements Runnable {
    private DatagramSocket ds;
 
    public ReceiveThread(DatagramSocket ds) {
        this.ds = ds;
    }
 
    @Override
    public void run() {
        try {
            while (true) {
                // 创建一个包裹
                byte[] bys = new byte[1024];
                DatagramPacket dp = new DatagramPacket(bys, bys.length);
 
                // 接收数据
                ds.receive(dp);
 
                // 解析数据
                String ip = dp.getAddress().getHostAddress();
                String s = new String(dp.getData(), 0, dp.getLength());
                System.out.println("from " + ip + " data is : " + s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
}
==============================================
运行效果
 
11.TCP协议发送数据(发送端为Client)
TCP协议发送数据:
  A:创建发送端的Socket对象
          这一步如果成功,就说明连接已经建立成功了。
  B:获取输出流,写数据
  C:释放资源
  
  连接被拒绝。TCP协议一定要先看服务器。(必须要有服务器,这一点与UDP不同)
  java.net.ConnectException: Connection refused: connect
 
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建发送端的Socket对象
        // Socket(InetAddress address, int port)//不用这种,为了书写简便
        // Socket(String host, int port)
        // Socket s = new Socket(InetAddress.getByName("192.168.12.92"), 8888);
        Socket s = new Socket("192.168.12.92", 8888);
 
        // 获取输出流,写数据
        // public OutputStream getOutputStream()
        OutputStream os = s.getOutputStream();
        os.write("hello,tcp,我来了".getBytes());
 
        // 释放资源
        s.close();
    }
}
======================================
 
12.TCP协议接收数据(Server端)
 
TCP协议接收数据:
  A:创建接收端的Socket对象
  B:监听客户端连接。返回一个对应的Socket对象
  C:获取输入流,读取数据显示在控制台
  D:释放资源
 
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 创建接收端的Socket对象
        // ServerSocket(int port)
        ServerSocket ss = new ServerSocket(8888);
 
        // 监听客户端连接。返回一个对应的Socket对象
        // public Socket accept()
        Socket s = ss.accept(); // 侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。
 
        // 获取输入流,读取数据显示在控制台
        InputStream is = s.getInputStream();
 
        byte[] bys = new byte[1024];
        int len = is.read(bys); // 阻塞式方法
        String str = new String(bys, 0, len);
 
        String ip = s.getInetAddress().getHostAddress();
 
        System.out.println(ip + "---" + str);
 
        // 释放资源
        s.close();
        // ss.close(); //这个不应该关闭,服务器不关闭
    }
}
==============================================
13.TCP协议发送和接收数据图解
下面是针对这张图的解析
客户端发送数据
 
服务器接收数据
服务器返回数据
14.服务器给客户端一个反馈案例
先写客户端
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 创建服务器Socket对象
        ServerSocket ss = new ServerSocket(9999);
 
        // 监听客户端的连接
        Socket s = ss.accept(); // 阻塞
 
        // 获取输入流(接收来自客户端的数据)
        InputStream is = s.getInputStream();
        byte[] bys = new byte[1024];
        int len = is.read(bys); // 阻塞
        String server = new String(bys, 0, len);
        System.out.println("server:" + server);
 
        // 获取输出流(对客户端做出应答)
        OutputStream os = s.getOutputStream();
        os.write("数据已经收到".getBytes());
 
        // 释放资源
        s.close();
        // ss.close();
    }
}
============================================================
再写客户端
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket s = new Socket("192.168.12.92", 9999);
 
        // 获取输出流
        OutputStream os = s.getOutputStream();
        os.write("今天天气很好,适合睡觉".getBytes());
 
        // 获取输入流
        InputStream is = s.getInputStream();
        byte[] bys = new byte[1024];
        int len = is.read(bys);// 阻塞
        String client = new String(bys, 0, len);
        System.out.println("client:" + client);
 
        // 释放资源
        s.close();
    }
}
=========================================================
注意:两个文件在同一个包下,运行的时候应该先运行Server的程序,再运行Client的程序
 
15.客户端键盘录入服务器控制台输出
需求:客户端键盘录入,服务器输出到控制台
为方便描述先写客户端
================================================
/*
 * 客户端键盘录入,服务器输出到控制台
 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket s = new Socket("192.168.12.92", 22222);
 
        // 键盘录入数据
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        // 把通道内的流给包装一下
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));
 
        String line = null;
        while ((line = br.readLine()) != null) {
            // 键盘录入数据要自定义结束标记
            if ("886".equals(line)) {
                break;
            }
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
 
        // 释放资源
        // bw.close(); //s关了,bw 自然关了
        // br.close();  //br通过886引发break语句关闭,不需要此句
        s.close();
    }
}
 
=============================================================

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 创建服务器Socket对象
        ServerSocket ss = new ServerSocket(22222);
 
        // 监听客户端连接
        Socket s = ss.accept();
 
        // 包装通道内容的流
        BufferedReader br = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        String line = null;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
 
        // br.close();//s关了,br自然关了
        s.close();
        // ss.close();//服务器不关闭
    }
}
======================================================
一个要注意的问题
16.客户端键盘录入服务器写到文本文件(而不是上一节的控制台)
/*
 * 客户端键盘录入,服务器输出文本文件
 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket s = new Socket("192.168.12.92", 23456);
 
        // 封装键盘录入
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        // 封装通道内的数据
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));
 
        String line = null;
        while ((line = br.readLine()) != null) {
            if ("over".equals(line)) {
                break;
            }
 
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
 
        // bw.close();
        // br.close();
        s.close();
    }
}
 
 
=============================================================
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 创建服务器Socket对象
        ServerSocket ss = new ServerSocket(23456);
 
        // 监听客户端连接
        Socket s = ss.accept();
 
        // 封装通道内的数据
        BufferedReader br = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        // 封装文本文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
 
        String line = null;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
 
        bw.close();
        // br.close();
        s.close();
        // ss.close();
    }
}
其实以上程序只要明确两点:数据从哪里来,数据要到哪里去
 
17.客户端读取文本文件服务器控制台输出案例
 
客户端文本文件,服务器输出到控制台
 
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建Socket对象
        Socket s = new Socket("192.168.12.92", 34567);
 
        // 封装文本文件
        BufferedReader br = new BufferedReader(new FileReader(
                "InetAddressDemo.java"));
        // 封装通道内的流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));
 
        String line = null;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
 
        br.close();
        s.close();
    }
}
======================================================
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 创建服务器Socket对象
        ServerSocket ss = new ServerSocket(34567);
 
        // 监听客户端连接
        Socket s = ss.accept();
 
        // 封装通道内的流
        BufferedReader br = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
 
        String line = null;
        while ((line = br.readLine()) != null) {
            System.out.println(line);//这里要手动加换行符
        }
 
 
        s.close();
    }
}
==================================================
 
18.TCP协议上传文本文件
客户端文本文件,服务器输出文本文件----其实就是客户端上传文件到服务器端
public class UploadClient {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket s = new Socket("192.168.12.92", 11111);
 
        // 封装文本文件
        BufferedReader br = new BufferedReader(new FileReader(
                "InetAddressDemo.java"));
        // 封装通道内流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));
 
        String line = null;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
 
        // 释放资源
        br.close();
        s.close();
    }
}
 
 ===================================================
public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 创建服务器端的Socket对象
        ServerSocket ss = new ServerSocket(11111);
 
        // 监听客户端连接
        Socket s = ss.accept();
 
        // 封装通道内的流
        BufferedReader br = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        // 封装文本文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java"));
 
        String line = null;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();//别忘记加换行
            bw.flush();
        }
 
        bw.close();
        s.close();
    }
}
=========================================================
19.TCP上传文本文件并给出反馈
首先,按照正常思路写
 
服务器接收完文件后给出反馈
然后客户端接收这个反馈信息
然后,发现,错了。。。。。。
会出现相互等待的现象,,,,虽然文件可以顺利上传,但是两个程序都没有正常终止
 
原因??
按照我们正常的思路加入反馈信息,结果却没反应。为什么呢?
  读取文本文件是可以以null作为结束信息的,但是呢,通道内是不能这样结束信息的。
  所以,服务器根本就不知道你结束了。而你还想服务器给你反馈。所以,就相互等待了。
 
如何解决呢?
    再多写一条数据,告诉服务器,读取到这条数据说明我就结束,你也结束吧。
          这样做可以解决问题,但是不好。(原因稍后会解析)
 
客户端
然而这样做并不好!!!文件中恰好含有终止符就有问题
 

接下来,查API寻求更好办法
Socket对象提供了一种解决方案
          public void shutdownOutput()
 
 
上图中正常连接终止序列也是数据的一部分,会传输过去但是接收端不会显示出来
 
最终版
public class UploadClient {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket s = new Socket("192.168.12.92", 11111);
 
        // 封装文本文件
        BufferedReader br = new BufferedReader(new FileReader(
                "InetAddressDemo.java"));
        // 封装通道内流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));
 
        String line = null;
        while ((line = br.readLine()) != null) { // 阻塞
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
 
        //自定义一个结束标记
//        bw.write("over");
//        bw.newLine();
//        bw.flush();
 
        //Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了
        s.shutdownOutput();
 
        // 接收反馈
        BufferedReader brClient = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        String client = brClient.readLine(); // 阻塞
        System.out.println(client);
 
        // 释放资源
        br.close();
        s.close();
    }
}
============================================================
public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 创建服务器端的Socket对象
        ServerSocket ss = new ServerSocket(11111);
 
        // 监听客户端连接
        Socket s = ss.accept();// 阻塞
 
        // 封装通道内的流
        BufferedReader br = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        // 封装文本文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java"));
 
        String line = null;
        while ((line = br.readLine()) != null) { // 阻塞
        // if("over".equals(line)){
        // break;
        // }
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
 
        // 给出反馈
        BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));
        bwServer.write("文件上传成功");
        bwServer.newLine();
        bwServer.flush();
 
        // 释放资源
        bw.close();
        s.close();
    }
}
=======================================================
20.TCP协议上传图片并给出反馈
上传图片与上传文件的区别:上传图片只能用字节流
public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 创建服务器Socket对象
        ServerSocket ss = new ServerSocket(19191);
 
        // 监听客户端连接
        Socket s = ss.accept();
 
        // 封装通道内流
        BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
        // 封装图片文件
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("mn.jpg"));
 
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
            bos.flush();
        }
 
        // 给一个反馈
        OutputStream os = s.getOutputStream();
        os.write("图片上传成功".getBytes());
 
        bos.close();
        s.close();
    }
}
===============================================
public class UploadClient {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket s = new Socket("192.168.12.92", 19191);
 
        // 封装图片文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                "林青霞.jpg"));
        // 封装通道内的流
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
 
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
            bos.flush();
        }
 
        s.shutdownOutput();
 
        // 读取反馈
        InputStream is = s.getInputStream();
        byte[] bys2 = new byte[1024];
        int len2 = is.read(bys2);
        String client = new String(bys2, 0, len2);
        System.out.println(client);
 
        // 释放资源
        bis.close();
        s.close();
    }
}
===================================================
注意:一定要注意---字节流也可以flush!!而且在本案例中,不flush的话会有问题
不加flush就会出现以下现象
查API
 
改进后
 
21.关于多个客户端上传到一个服务器的思考
之前的程序都是一对一的,假如有多个客户端怎么办??
正常思路:在服务器端用循环改进
 
但是
通过while循环可以改进一个服务器接收多个客户端。
 但是这个是有问题的。
 如果是这种情况,假设我还有张三,李四,王五这三个人分别执行客户端
 张三:好好学习.avi(100M)            256k(带宽,下面同理)
 李四:天天向上.mp3(3M)                1M
 王五:ILoveJava.txt(1k)            100M
 
带宽不同,会导致第一个张三上传了一年文件然后才可以让李四上传
改进:利用多线程(下一节)
==========================================
22.多线程改进多个客户端上传文件案例
客户端
public class UploadClient {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket s = new Socket("192.168.12.92", 11111);
 
        // 封装文本文件
        // BufferedReader br = new BufferedReader(new FileReader(
        // "InetAddressDemo.java"));
        BufferedReader br = new BufferedReader(new FileReader(
                "ReceiveDemo.java"));
        // 封装通道内流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                s.getOutputStream()));
 
        String line = null;
        while ((line = br.readLine()) != null) { // 阻塞
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
 
        // Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了
        s.shutdownOutput();
 
        // 接收反馈
        BufferedReader brClient = new BufferedReader(new InputStreamReader(
                s.getInputStream()));
        String client = brClient.readLine(); // 阻塞
        System.out.println(client);
 
        // 释放资源
        br.close();
        s.close();
    }
}
=================================================
新的线程(其实就是把大部分的服务器代码粘贴过来)
public class UserThread implements Runnable {
    private Socket s;
 
    public UserThread(Socket s) {
        this.s = s;
    }
 
    @Override
    public void run() {
        try {
            // 封装通道内的流
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    s.getInputStream()));
            // 封装文本文件
            // BufferedWriter bw = new BufferedWriter(new
            // FileWriter("Copy.java"));
 
            // 为了防止名称冲突
            String newName = System.currentTimeMillis() + ".java";
            BufferedWriter bw = new BufferedWriter(new FileWriter(newName));
 
            String line = null;
            while ((line = br.readLine()) != null) { // 阻塞
                bw.write(line);
                bw.newLine();
                bw.flush();
            }
 
            // 给出反馈
            BufferedWriter bwServer = new BufferedWriter(
                    new OutputStreamWriter(s.getOutputStream()));
            bwServer.write("文件上传成功");
            bwServer.newLine();
            bwServer.flush();
 
            // 释放资源
            bw.close();
            s.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
=====================================================
服务器端测试
public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 创建服务器Socket对象
        ServerSocket ss = new ServerSocket(11111);
 
        while (true) {
            Socket s = ss.accept();
            new Thread(new UserThread(s)).start();
        }
    }
}
=================================================
其实这个案例只是简单模拟,并不很理想
以下解析:
while true允许多个客户端同时访问
 
时间值保证命名的唯一性(其实也不太靠谱,只是简单模拟,因为很可能同一毫秒挤进很多线程)
 
day26笔记补充
      端口
            是应用程序的标识。范围:0-65535。其中0-1024不建议使用。
       协议
            UDP:数据打包,有限制,不连接,效率高,不可靠
            TCP:建立数据通道,无限制,效率低,可靠
  Socket机制
        A:通信两端都应该有Socket对象
        B:所有的通信都是通过Socket间的IO进行操作的
======================================================
  UDP协议发送和接收数据(掌握 自己补齐代码)
        发送
            创建UDP发送端的Socket对象
            创建数据并把数据打包
            发送数据
            释放资源
 
        接收
            创建UDP接收端的Socket对象
            创建数据包用于接收数据
            接收数据
            解析数据包
            释放资源
=======================================
    TCP协议发送和接收数据(掌握 自己补齐代码)
        发送
            创建TCP客户端的Socket对象
            获取输出流,写数据
            释放资源
 
        接收
            创建TCP服务器端的Socket对象
            监听客户端连接
            获取输入流,读取数据
            释放资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值