high-level——网络编程(12)

网络编程

一、通信

  • 两个节点实现相互间的数据传输。
  1. 线程间的通信,比较简单,是指同一进程中的不同线程,线程间共享内存空间,且由同一jvm来进行管理。

    • java线程间的通讯有三种方式,其中之一就是通过流来实现通讯。

      package com.zhong.test_7;
      
      import java.io.IOException;
      import java.io.PipedInputStream;
      import java.io.PipedOutputStream;
      
      public class PipeTest {
          public static void main(String[] args) {
              PipedInputStream input = new PipedInputStream();
              PipedOutputStream output = new PipedOutputStream();
              //流连接操作
              try {
                  input.connect(output);
              } catch (IOException e) {
                  e.printStackTrace();
              }
              //启动线程
              new Thread(new Producer(output)).start();
              new Thread(new Consumer(input)).start();
      
          }
      
          static class Consumer implements Runnable {//消费者
              private PipedInputStream input;
      
              public Consumer(PipedInputStream input) {
                  this.input = input;
              }
      
              @Override
              public void run() {
                  while (true) {
                      try {
                          Thread.sleep(500);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      try {
                          int count = input.available();//得到输入流中等待读取的数据的数量
                          if (count > 0) {
                              System.out.println("待取用的数据数量:" + count);
                              System.out.println("读出的数据:" + input.read());
                          }
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
      
          static class Producer implements Runnable {//生产者
              private PipedOutputStream output;
      
              public Producer(PipedOutputStream output) {
                  this.output = output;
              }
      
              @Override
              public void run() {
                  while (true) {
                      for (int i = 0; i < 5; i++) {
                          try {
                              output.write(i);
                          } catch (IOException e) {
                              e.printStackTrace();
                          }
                      }
                      try {
                          Thread.sleep(5000);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
      }
      
  2. 进程间的通信,比较复杂,两个进程可以处于不同的主机,操作系统可能不同,它们的内存是独立的。

二、网络编程

  • 在不同的进程间实现通信。
  • 依据通信的方式把现代的软件分成两大部分:
    • C/S 模式,客户/服务器,每个客户都是一个进程,服务器也是一个进程,C表示在客户机上安装的应用程序。(界面可以很复杂,但是客户机需要安装软件),给软件的使用带来了一定的不便。
    • B/S 模式,浏览器/服务器模型,客户机都是通过浏览器与服务器实现通讯,客户机不用安装应用程序。(界面不会太复杂,通讯的效率不高,不需要安装软件,使用很方便)
  • java 应用程序的通信模型:
    • 采用 C/S 的通讯模型。客户的请求与服务器的响应有先后次序,不能颠倒,这样一去一回是一个完整的过程。此过程可以无限的延续下去。

三、通信协议

  • 进程之间通信必须基于某种通信协议来完成。
  • 通信协议是通信双方都应该遵守的规则,它规定了通信双方在数据的格式、速度、先后顺序等方面的一些要求。
  • 通信双方所执行的功能及执行顺序可以用基于OSI标准的七层通讯模型来描述,它概括了几乎所有的通信结构。
  • 从七层模型中抽取出四层,称为通信的四层模型,基本上可以概括 java 程序的通讯结构。
  1. TCP/IP 协议

    • TCP 是传输控制协议,工作在传输层;IP 是网际协议,工作在网络层。
    • TCP是互联网通信的基础协议,它出现的原因就是为互联网的通信提供一种标准。
    • TCP协议的特点:
      1. 它是基于连接的,通信节点之间首选先要建立稳固的连接。
      2. 它是可靠的,可以不中断,完整的,连续的把数据传递给对方。
  2. UDP 协议

    • 数据报通信协议,也称为直连协议。
    • 特点:通信双方不用建立连接,把发送方和接收方的地址都放在数据包中,把数据包向网络上发送,可能会出现丢包。它是不可靠的传输协议。

    总结:以上两个协议都属于底层协议,通信中使用最多的是 TCP 协议。

  3. 通讯端口

    • 同一主机中的不同进程和不同主机间的进程都可以实现通讯,一台主机上有多个进程,为了区分不同的进程,需要给通讯的进程分配一个编号,此编号就是端口号。所以,需要进行通讯的进程都必须要有一个唯一的端口号。端口号由两个字节表示,范围是 0-65535,0-1023 这个范围被一些公共应用程序所占据,所以给程序分配的端口号要大于1023。如果发生冲突将不能正常通讯。
  4. 实现TCP协议通讯的三要素

    • 通讯协议,IP地址,端口号。缺一不可。

四、基于 tcp/ip 协议的通信编程

  1. 需要一个服务器的进程,同时也至少需要一个客户端的进程。
  2. java 的 net 包中提供了两个重要的 api,服务器端需要使用 ServerSocket(服务端套接字),客户端需要使用 Socket(套接字)。
  3. 通信的过程就是数据相互传输的过程,所以服务器和客户端都需要通过网络进行数据的读写,因此基本套接字可以获取用来进行读写操作的流对象。
  4. 如果服务器需要与多个客户端来实现即时通讯,在服务器端需要创建多个线程,每个线程各自负责一个客户端的通信任务。

五、实现单个客户端与服务器的通信

  • public class ServerSocket 类

    • 作用:接收客户端的请求,并创建服务器端与客户端的连接,然后返回一个套接字对象(Socket),服务器使用此套接字对象来与客户端进行数据的读写操作。
  • public class Socket 类

    • 作用:创建客户端的套接字,客户端需要创建一个 Socket 对象,此对象就是客户端套接字,客户端套接字会与服务器端的套接字形成了通信的两个端点。
  • 基本例子:

    package com.zhong.test_7;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class ServerSocketDemo {
        public static void main(String[] args) throws IOException, InterruptedException {
            //创建服务器套接字
            ServerSocket ssk = new ServerSocket(8989);
            System.out.println("服务器在8989端口进行监听……");
            //利用服务器套接字来监听客户端的请求,程序阻塞,等待请求到达
            Socket socket = ssk.accept();
            System.out.println("本地的ip及端口:" + socket.getLocalAddress() + " " + socket.getLocalPort());
            System.out.println("服务器的ip及端口:" + socket.getInetAddress() + " " + socket.getPort());
            //接收到客户端的请求,创建socket套接字对象,获取输入流对象,等待接收客户的数据
            InputStream is = socket.getInputStream();
            System.out.println("已接收到客户端的连接请求,并建立了连接");
            System.out.println("已拿到输入流,等待接收客户端的信息");
            //准备读取客户端的数据
            int len;
            byte[] b = new byte[1024];
            //解除阻塞,处理客户的数据
            len = is.read(b);//程序在此阻塞,等待数据的到达
            System.out.println("客户端说:" + new String(b, 0, len));
            //服务器向客户端回话
            OutputStream os = socket.getOutputStream();
            os.write("你好!".getBytes());
            System.out.println("发送完毕");
            socket.close();
            ssk.close();
        }
    }
    
    package com.zhong.test_7;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    
    public class SocketDemo {
        public static void main(String[] args) throws IOException, InterruptedException {
            //创建客户端套接字
            Socket sk = new Socket("127.0.0.1", 8989);
            System.out.println("本地的ip及端口:" + sk.getLocalAddress() + " " + sk.getLocalPort());
            System.out.println("服务器的ip及端口:" + sk.getInetAddress() + " " + sk.getPort());
            //sk创建成功,则两端的连接成功建立
            //得到输出流对象,向服务器发送信息
            OutputStream os = sk.getOutputStream();
            System.out.println("已建立与服务器的连接,向服务器发信息。");
            os.write("hello word!".getBytes());//不会阻塞
            InputStream is = sk.getInputStream();
            byte[] b = new byte[1024];
            int len = is.read(b);//此处会阻塞等待信息到达
            System.out.println("客户端正在等待服务器的信息:");
            System.out.println("服务器说:" + new String(b, 0, len));
            System.out.println(new String(b, 0, len));
            sk.close();
        }
    }
    
  • 文件上传

    package com.zhong.test_7;
    
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class FileServer {
        public static void main(String[] args) throws IOException {
            FileOutputStream fos = new FileOutputStream("E:\\IDEA project\\src\\com\\zhong\\test_7\\new.txt");
            ServerSocket ssk = new ServerSocket(2520);
            Socket sk = ssk.accept();
            InputStream is = sk.getInputStream();
            byte[] b = new byte[1024];
            int len;
            System.out.println("等待接收文件…………");
            while ((len = is.read(b)) != -1) {
                fos.write(b, 0, len);
                fos.flush();
            }
            System.out.println("文件接收完毕");
            fos.close();
            sk.close();
        }
    }
    
    package com.zhong.test_7;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.net.Socket;
    
    public class FileClient {
        public static void main(String[] args) throws IOException {
            Socket sk = new Socket("localhost", 2520);
            FileInputStream fis = new FileInputStream("F:\\我的文件\\常用系统命令及快捷键.txt");
            int len;
            byte[] b = new byte[1024];
            System.out.println("开始发送文件…………");
            while ((len = fis.read(b)) != -1) {
                sk.getOutputStream().write(b, 0, len);
            }
            System.out.println("文件上传完毕");
            fis.close();
            sk.close();
        }
    }
    
  • 互相不间断的发送信息

    package com.zhong.test_7;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Scanner;
    
    public class SessionServer {
        public static void main(String[] args) throws IOException {
            ServerSocket ssk = new ServerSocket(8989);
            System.out.println("服务器启动……");
            Socket sk = ssk.accept();
            System.out.println("已有客户连接");
            BufferedReader br = null;
            PrintWriter pw = null;
            String word;
            Scanner sc = new Scanner(System.in);
            while (true) {
                br = new BufferedReader(new InputStreamReader(sk.getInputStream()));
                System.out.println("服务器在等待消息……");
                word = br.readLine();
                System.out.println("客户说:" + word);
                if (word.equals("再见")) break;
                pw = new PrintWriter(sk.getOutputStream(), true);
                System.out.print("服务器说:");
                word = sc.next();//阻塞等待用户输入
                pw.println(word);
                pw.flush();
                if (word.equals("再见")) break;
            }
            pw.close();
            br.close();
            sk.close();
            ssk.close();
        }
    }
    
    package com.zhong.test_7;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.util.Scanner;
    
    public class SessionClient {
        public static void main(String[] args) throws IOException {
            Socket sk = new Socket("127.0.0.1", 8989);
            Scanner sc = new Scanner(System.in);
            PrintWriter pw = null;
            BufferedReader br = null;
            String word;
            while (true) {
                pw = new PrintWriter(sk.getOutputStream(), true);
                System.out.print("客户说:");
                word = sc.next();
                pw.println(word);
                pw.flush();
                if (word.equals("再见")) break;
                System.out.println("客户在等待消息……");
                br = new BufferedReader(new InputStreamReader(sk.getInputStream()));
                word = br.readLine();
                System.out.println("服务器说:" + word);
                if (word.equals("再见")) break;
            }
            pw.close();
            br.close();
            sk.close();
        }
    }
    
  • QQ聊天(私聊和群聊)

    package com.zhong.test_8;
    
    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Calendar;
    
    public class QQServer {
        static private ArrayList<User> userRegister = new ArrayList<>();
    
        public static void main(String[] args) {//主线程,管理线程
            ServerSocket ssk = null;
            try {
                ssk = new ServerSocket(3434);
                while (true) {
                    System.out.println("等待客户端的连接请求……");
                    Socket sk = ssk.accept();//sk负责与此次连接的客户端进行通信
                    BufferedReader br = new BufferedReader(new InputStreamReader(sk.getInputStream()));
                    String userName = br.readLine();
                    User user = new User(userName, sk);
                    userRegister.add(user);//把sk套接字放到集合中
                    new Thread(new UserThread(user)).start();
                }
            } catch (Exception e) {
                System.out.println("有客户断开连接。");
            }
        }
    
        static private boolean isClose(Socket sk) {//判断是否断开连接
            try {
                sk.sendUrgentData(0xff);
                return false;
            } catch (Exception e) {
                return true;
            }
        }
    
        //服务端为各用户提供通信服务的用户线程
        //其任务一是接收对应客户端的信息(读),把其他客户端传来的信息发送给本客户端(写)
        static class UserThread implements Runnable {
            private User user;
            private ObjectInputStream readStream;
    
            public UserThread(User user) throws IOException {
                this.user = user;//本线程接收套接字对象
                readStream = new ObjectInputStream(user.getSk().getInputStream());//创建本线程的输入流对象
            }
    
            @Override
            public void run() {
                while (true) {
                    if (isClose(user.getSk())) {
                        System.out.println("用户" + user.getUserName() + " 已断开!");
                        userRegister.remove(user);
                        return;
                    } else {
                        System.out.println(Thread.currentThread().getName() + "等待接收信息");
                        try {
                            Message message = (Message) readStream.readObject();//会阻塞,等待信息的到达
                            System.out.println(message);
                            boolean isExist = false;
                            boolean all = false;
                            //把信息发送给接收方(对应客户端),先拿到对方的Socket,再写回客户端
                            for (User user : userRegister) {
                                if (message.getReceive().equals(this.user.getUserName()) && !message.getReceive().equals("all")) {
                                    String date = new SimpleDateFormat("yyyy年/MM月/dd日 HH:mm:ss").format(Calendar.getInstance().getTime());
                                    Message msg = new Message("系统", user.getUserName(), "不可以自言自语", date);
                                    ObjectOutputStream oos = new ObjectOutputStream(user.getSk().getOutputStream());
                                    oos.writeObject(msg);
                                    isExist = true;
                                    System.out.println("系统给 " + user + " 发了一条一级警告");
                                    // oos.close();
                                    break;//如果是本线程的套接字,则不发送信息
                                }
                                if (message.getReceive().equals("all")) {//群发
                                    ObjectOutputStream writeStream = new ObjectOutputStream(user.getSk().getOutputStream());//得到对方的套接字对象
                                    if (!all) {
                                        message.setDate(message.getDate() + " 全体消息");
                                        all = true;
                                    }
    
                                    writeStream.writeObject(message);
                                    System.out.println(user + "发送信息到了客户端");
                                    isExist = true;
                                    //  writeStream.close();
                                }
                                if (message.getReceive().equals(user.getUserName())) {//单独发
                                    ObjectOutputStream writeStream = new ObjectOutputStream(user.getSk().getOutputStream());//得到对方的套接字对象
                                    writeStream.writeObject(message);
                                    System.out.println(user + "发送信息到了客户端");
                                    isExist = true;
                                    //  writeStream.close();
                                    break;
                                }
                            }
                            if (!isExist) {
                                String date = new SimpleDateFormat("yyyy年/MM月/dd日 HH:mm:ss").format(Calendar.getInstance().getTime());
                                Message msg = new Message("系统", user.getUserName(), "对不起,查无此人或此人已下线!", date);
                                ObjectOutputStream oos = new ObjectOutputStream(user.getSk().getOutputStream());
                                oos.writeObject(msg);
                                System.out.println("系统给 " + user + " 发了一条二级警告");
                                //  oos.close();
                            }
                        } catch (IOException | ClassNotFoundException e) {
                            e.printStackTrace();
                        }
    
                    }
                }
    
            }
        }
    
    }
    
    package com.zhong.test_8;
    
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.Scanner;
    
    public class QQClient {
        public static void main(String[] args) throws IOException, ClassNotFoundException {//主线程负责接收信息
            Socket sk = new Socket("127.0.0.1", 3434);//创建客户端套接字,向服务器发首次连接请求
            Scanner sc = new Scanner(System.in);
            PrintWriter pw = new PrintWriter(sk.getOutputStream(), true);
            System.out.print("请输入您的名字:");
            String userName = sc.next();
            pw.println(userName);
            pw.flush();
    
            User user = new User(userName, sk);
            ObjectInputStream ois;
            new Thread(new UserWrite(user)).start();//启动写信息的子线程
            while (true) {//主线程中负责接收服务器发来的信息,该信息来源于其他的客户端
                ois = new ObjectInputStream(sk.getInputStream());//得到输入流对象
                Message msg = (Message) ois.readObject();//阻塞,等待信息到达
                System.out.println(msg);
                System.out.print(user.getUserName() + "(你):");//输出输入提示语
            }
        }
    
        static class UserWrite implements Runnable {
            private User user;
            private ObjectOutputStream oos;
    
            public UserWrite(User user) throws IOException {
                this.user = user;
                oos = new ObjectOutputStream(user.getSk().getOutputStream());//创建发信息的输出流对象
            }
    
            @Override
            public void run() {
                Scanner sc = new Scanner(System.in);//接收用户的输入
                System.out.print("接收人(all表示全体):");
                String receive = sc.next();
                while (true) {//不断的书写信息并发送
                    System.out.print(user.getUserName() + "(你):");//输出输入提示语
                    String word = sc.next();//接收控制台的输入,阻塞
                    String date = new SimpleDateFormat("yyyy年/MM月/dd日 HH:mm:ss").format(Calendar.getInstance().getTime());
                    //把输入内容封装到信息对象中
                    Message msg = new Message(user.getUserName(), receive, word, date);
                    try {
                        if (!receive.equals("all"))
                            System.out.println(msg.getSender() + "(" + msg.getDate() + "):" + msg.getWord());
                        oos.writeObject(msg);//发送信息
                        oos.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Æ_华韵流风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值