网络通信的实现

1.如何建立两个节点(电脑)之间的网络连接?

2.如何向另外一个节点(电脑)发送信息?

3.如何从外部节点(电脑)接收一个请求并给预响应?

4.如何利用网络协议(TCP,UDP)?


网络通信协议七层模型

OSI(Open System Interconnection)

 

TCP/IP协议

TCP/IP协议(定义了电子设备(比如计算机)如何连入因特网,以及数据如何在它们之间传输的标准)。

IP : ip地址唯一 确定与哪台电脑通信 

        四个字节 每个字节用 “ . ”分割 256的四次方个ip地址 每一位的范围: 0~255

域名  英文符号替换IP 为方便记忆

         映射: www.baidu.com 绑定一个IP ,访问百度根据域名

          可通过域名解析器实现域名转换ip

端口:端口号 0~65535个 确定与这台电脑的哪个程序通信

  • 网络是什么?

        网络是信息传输、接收、共享的虚拟平台。

        网络是用物理链路将各个孤立的工作站或主机相连在一起,组成数据链路,从而达到资源共享和通信的目的。

        网络通信是两台或多台电脑之间能进行连接、信息交互。

广域网(外网)、局域网(内网)


TCP 协议通信

TCP协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。

 面向连接:发送数据前三次握手建立连接 

         如:打电话 必须双方连接后才能打

        

可靠性:有序到达,重发机制

如何建立连接? 三次握手

 

第一次握手: 确定客户端发送能力

第二次握手:确定服务器端接收能力,发送能力

第三次握手:确定客户端接收能力


使用TCP协议实现通信,需要使用流式套接字。

即客户端采用socket,而服务器端采用ServerSocket来完成通信的方式。

  • 什么是socket?

        套接字 相当于插座,通过它发送给接收数据(电流)

        

套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。

当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取(字节流)来进行通信。

ServerSocket :等待客户端的连接

        ServerSocket(int port)创建未绑定的服务器套接字

        accept() 监听端口 等待客户端的连接 一旦有客户端的连接,得到与客户端一对一的 Socket

Sokect(网络地址,端口号) 构造方法

  1. 流式套接字 TCP 面向连接
  2. 数据报式套接字 UDP通信
  3. 原始式套接字 IP直接访问

基本流程

服务器端

  1. 创建一个ServerSocket

  2. 调用accept()等待客户端连接

  3. 得到客户端的Socket对象,调用getInputStream()、getOutputStream()得到输入输出流

  4. 输入流接收数据,输出流发送数据

  5. close() 关闭资源

客户端

  1. 创建Socket,请求服务器地址,端口,与服务器进行连接

  2. 调用getInputStream()、getOutputStream()得到输入输出流

  3. 输出流接收数据,输入流发生数据

  4. close()

启动顺序:先启动服务器,再启动客户端

 数据流:

 服务器端

package Send;
​
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
​
public class Server {
    public static void main(String[] args) throws IOException {
​
        ServerSocket serverSocket = new ServerSocket(8888);
        Socket client = serverSocket.accept();//阻塞
        System.out.println("有客户端连接");
        //网络流
        InputStream in = client.getInputStream();
        OutputStream out = client.getOutputStream();
        BufferedReader br= null;
        BufferedWriter bw = null;
        br = new BufferedReader(new InputStreamReader(in));
        bw = new BufferedWriter(new OutputStreamWriter(out));
​
        //接收数据
        String line = br.readLine();//阻塞方法 读到行结尾 换行符
        System.out.println("客户端说:"+line);
​
        //发送数据
        bw.write("你好客户端!我是服务器!");
        bw.newLine();//换行符
        bw.flush();//刷新缓冲区
​
        //关闭资源
        bw.close();
        br.close();
        serverSocket.close();
    }
}
​

客户端

package Send;
​
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
​
public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",8888);
​
        //发送数据
        InputStream in = socket.getInputStream();
        OutputStream out = socket.getOutputStream();
        BufferedReader br= null;
        BufferedWriter bw = null;
        br = new BufferedReader(new InputStreamReader(in));
        bw = new BufferedWriter(new OutputStreamWriter(out));
        //发送数据
        Scanner input = new Scanner(System.in);
        bw.write(input.next());
       // bw.write("你好服务器!我是客户端!");
        bw.newLine();//换行符
        bw.flush();//刷新缓冲区
​
        //接收数据
        String line = br.readLine();//阻塞方法 读到行结尾 换行符
        System.out.println("服务端说:"+line);
​
        //关闭资源
        bw.close();
        br.close();
        socket.close();
    }
}

实现多个客户端连接

package Send;

import java.io.*;
import java.net.Socket;

public class Server2Thread extends Thread{
    private Socket client;

    //client 属性赋值: 1. set方法 2. 构造方法
    public Server2Thread(String name,Socket client) {
        super(name);
        this.client = client;
    }

    @Override
    public void run() {
        BufferedReader br = null;
        BufferedWriter bw = null;

        try {
            InputStream in = client.getInputStream();
            OutputStream out = client.getOutputStream();
            br = new BufferedReader(new InputStreamReader(in));
            bw = new BufferedWriter(new OutputStreamWriter(out));

            //接收数据
            String line = br .readLine();
            System.out.println(Thread.currentThread().getName()+"说:"+ line);
            System.out.println();
            //发送数据
            bw.write("你好 客户端");
            bw.newLine();//换行符
            bw.flush();//刷新缓存区

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //关闭资源
                if (bw != null) bw.close();
                if (br != null) br.close();
                if (client != null) client.close();
            }catch (IOException e){

            }
        }
    }
}
package Send;

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

public class Server2 {//一个服务器允许连接多个客户端

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        int i =1;
        while (true){
            Socket client = serverSocket.accept();//阻塞
            System.out.println("有客户端连接");
            //有一个客户端连接,创建一个线程与该客户端连接
            Server2Thread server2Thread = new Server2Thread("客户端"+i,client);
            //启动线程
            server2Thread.start();
            i++;
        }

    }
}

UDP协议通信

UDP是一个 面向无连接,不可靠的传输层协议。

数据包套接字 面向无连接 类似于 :发短信 管你接不接收

DatagramSocket 发送或接收数据包的套接字,不维护连接状态,不产生输入输出流

DatagramPacket 数据包 封装了数据、数据长度

DatagramPacket(byte[] buf, int length) 构造一个 DatagramPacket用于接收长度的数据包 length
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号。
package UDPTest;
​
import java.io.IOException;
import java.net.*;
​
public class Client {
    public static void main(String[] args) {
​
        //随机绑定端口
        try {
            //创建一个用于发送接收的socket
            DatagramSocket datagramSocket = new DatagramSocket();
​
            //发送数据包
            String str = "出任务出任务";
            byte[] content = str.getBytes();
            try {
                //第三个参数,ip地址封装的对象 类型InetAddress,是接受方的地址
                // 第四个参数,对方的端口号
                DatagramPacket dp1 = new DatagramPacket(content, content.length, InetAddress.getLocalHost(),7777);
                //发送
                datagramSocket.send(dp1);
                //接收
                byte[] replay = new byte[1024];
                DatagramPacket dp2 = new DatagramPacket(replay, replay.length);
                datagramSocket.receive(dp2);
                String msg = new String(replay,0,dp2.getLength());
                System.out.println("我是client1,收到消息:"+msg);
            } catch (UnknownHostException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
​
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }
}
​
package UDPTest;
​
import java.io.IOException;
import java.net.*;
​
public class Client2 {
    public static void main(String[] args) {
        //随机绑定端口
        try {
            DatagramSocket datagramSocket = new DatagramSocket(7777);
​
            try {
                //接收
                byte[] replay = new byte[1024];
                DatagramPacket dp2 = new DatagramPacket(replay, replay.length);
                datagramSocket.receive(dp2);
                String msg = new String(replay,0,replay.length);
                System.out.println("我是client2,接收到消息:"+msg);
​
                //发送
                String str = "马上马上";
                byte[] content = str.getBytes();
                //获取对方ip和端口
           //     dp2.getPort();//本机端口
           //     dp2.getAddress().getHostName();//获取本机地址
               // InetSocketAddress address = (InetSocketAddress) dp2.getSocketAddress(); //获取远程地址
                // DatagramPacket dp1 = new DatagramPacket(content, content.length,address);
​
                InetAddress ia = dp2.getAddress();
                int port = dp2.getPort();
                DatagramPacket dp1 = new DatagramPacket(content, content.length,ia,port);
                datagramSocket.send(dp1);
​
                datagramSocket.close();
            } catch (UnknownHostException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
​
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }
}
​

利用网络协议(TCP,UDP)

  • 某公司想组建本公司的客服聊天系统,用于客户与客服人员的点对点的对话,当客户连接到客服服务器时, 客户(客户端)就可以向客服人员(服务器端)进行咨询

发送消息的线程

package Send;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;

public class SendThread extends Thread{
    private Socket socket;
    private boolean flag = true;
    Scanner input = new Scanner(System.in);

    public SendThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        while (flag){
            send(input.next());
        }
    }

    //发送
    public void send(String str){
        OutputStream out = null;
        BufferedWriter bw = null;
        try {
            out = socket.getOutputStream();
            bw = new BufferedWriter(new OutputStreamWriter(out));
            bw.write(str);
            bw.newLine();
            bw.flush();
        } catch (IOException e) {
            flag = false;
            CloseUtil.close(bw,socket);
        }
    }
}

接收消息的线程

package Send;

import java.io.*;
import java.net.Socket;

public class ReceiveThread extends Thread{
    private Socket socket;
    private  boolean flag = true;
    public ReceiveThread(String name,Socket socket) {
        super(name);
        this.socket = socket;
    }

    @Override
    public void run() {
        while (flag){
            String receive = receive();
            System.out.println(receive);
        }
    }

    public  String  receive()  {
        InputStream in = null;
        BufferedReader br =null;
        try {
            in = socket.getInputStream();
            br =new BufferedReader(new InputStreamReader(in));
            String line = br.readLine();
            return this.getName()+"说:"+line;
        } catch (IOException e) {
            flag = false;
            CloseUtil.close(br,socket);
        }
        return null;
    }
}

关闭资源的工具类

package Send;

import java.io.Closeable;

public class CloseUtil {
    public static void close(Closeable... closeables){ //可变参数
        if (closeables != null && closeables.length > 0){
            if (closeables != null){
                closeables.clone();
            }
        }

    }
}

客户端

package Send;

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

public class Client3 {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",8888);
        new SendThread(socket).start();
        new ReceiveThread("服务器",socket).start();
    }
}

服务端线程

package Send;

import java.net.Socket;

public class ServerThread3 extends Thread{
    private Socket client;

    public ServerThread3(String name,Socket client) {
        super(name);
        this.client = client;
    }

    @Override
    public void run() {
        //创建发送线程 写
        new SendThread(client).start();
        //创建接收线程 读
        new ReceiveThread(this.getName(),client).start();
    }
}

服务端

package Send;

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

//用户端和客服端之间需要进行对话
//需要实现一次发送多条消息,同时也可以接受多条消息  通过true设置死循环 能够一直读写

public class Server3 {//点对点  缺点:一对一 一个服务器面对多个客户端 可以接收多个客户端信息,但发送不了给多个不知道发给谁(客人接收不到)
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        int i =1;
        while (true){
            Socket client = serverSocket.accept();//阻塞
            System.out.println("有客户端连接");
            //有一个客户端连接,创建一个线程与该客户端连接
            ServerThread3 serverThread3 = new ServerThread3("客户端"+i,client);
            //启动线程
            serverThread3.start();
            i++;
        }

    }
}

 


  • 聊天室2.0 多对多 

优化服务器线程代码 实现服务器接收客户端的消息,再将消息写出给其他客户端

package Chat;

import java.io.*;
import java.net.Socket;
import java.util.List;

public class ServerThread4 extends Thread{
    private Socket socket;

    public ServerThread4(String name, Socket socket) {
        super(name);
        this.socket = socket;
    }

    @Override
    public void run() {
        //先读后发 先执行括号内
        while (true){
            sendOther(receive());
        }

    }
    public String receive(){
        InputStream in = null;
        BufferedReader br =null;
        try {
            in = socket.getInputStream();
            br =new BufferedReader(new InputStreamReader(in));
            String line = br.readLine();
            return this.getName()+"说"+line;
        } catch (IOException e) {
            CloseUtil.close(br,socket);
        }
        return "";
    }

    //发送 转发送给其他人
    public void sendOther(String str){
        List<Socket> socketList = Server4.socketList;
        for (Socket client : socketList){
            //排除自己
            if (client != socket){
                send(str,client);
            }
        }
    }

    private void send(String str, Socket client) {
        OutputStream out = null;
        BufferedWriter bw = null;
        try {
            out = client.getOutputStream();
            bw = new BufferedWriter(new OutputStreamWriter(out));
            bw.write(str);
            bw.newLine();
            bw.flush();
        } catch (IOException e) {

            CloseUtil.close(bw,client);
        }
    }
}

 

  • 在线阅读小说 

服务器

客户端: 显示菜单(输入)

功能:

  1. 上传小说

客户端服务器
连接服务器,创建Socket接收客户端连接
接收提示,打印 输入 发送小说名发送 “请输入要上传的小说名”
使用本地缓冲输入流,读取客户端硬盘上传文件 循环读:读一行,通过网络IO输出流发给服务器接收小说名 保存小说名 集合
服务器循环读取客户端的数据 创建本地输出流 保存到硬盘 追加模式
小说上传完成 客户端发送“exit”服务器接收“exit” 退出循环
关闭资源
  1. 在线阅读

客户端服务器

连接服务器,创建Socket

发送在线阅读指令 2

接收客户端连接
接收服务器发送的小说列表 并打印

发送小说名列表 (字符串 或对象流 序列化 持久化 

SerialVersionUID 序列化常量 ObjectOutputStream:对对象进行序列化

选择需要阅读的小说序号

发送给服务器

接收客户端要阅读的小说序号,通过序号找到对应小说

创建本地输入流 读取对应的小说(100行)

循环读 循环写 发送给客户端

接收服务器的发送小说内容

在控制台打印

关闭资源

上传路径:原文件夹——客户端——服务器——目标文件夹

前100行  用while循环 k++ 到100行就发送exit 客户端接收到exit就停止打印

客户端

package readBook;

import java.io.*;
import java.net.Socket;
import java.util.List;
import java.util.Scanner;

public class Client {
    Scanner input = new Scanner(System.in);
    SendUtil sendUtil = new SendUtil();
    public static void main(String[] args) {
        while (true){
            new Client().showMenu();
        }
    }

    public void showMenu(){
        System.out.println("欢迎访问xxxx小说网站");
        System.out.println("\t1.上传小说");
        System.out.println("\t2.在线阅读");
        System.out.println("\t3.退出");
        System.out.println("请选择:");
        int choose = input.nextInt();
        switch (choose){
            case 1:
                //上传小说
                upload();
                break;
            case 2:
                //在线阅读
                readOnline();
                break;
            case 3:
                System.out.println("感谢使用");
                System.exit(1);
            default:
                showMenu();
        }
    }

    private void readOnline() {
        Socket socket =null;
        BufferedReader br = null;
        BufferedWriter bw = null;

        try {
            socket = new Socket("127.0.0.1",8888);
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

            //发送上传的指令
            sendUtil.sendWrite(bw,"2");
            String nameList = br.readLine();
            if (nameList.equals("没有书籍!")){
                System.out.println("请去上传书籍");
                showMenu();
            }else {
                System.out.println(nameList);
                System.out.println("请选择需要阅读的小说序号:");
                sendUtil.sendWrite(bw,input.next());

                String content =null;
               while (!(content=br.readLine()).equals("exit")){
                   System.out.println(content);
               }
            }


        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            CloseUtil.close(br,bw,socket);
        }
        showMenu();
    }

    //上传小说
    private void upload() {
        //连接服务器
        Socket socket =null;
        BufferedReader br = null;
        BufferedWriter bw = null;
        //本地输入流
        BufferedReader br2= null;
        try {
           socket = new Socket("127.0.0.1",8888);

          br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
          bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

          //发送上传的指令
            sendUtil.sendWrite(bw,"1");

          //接收服务器信息
            String line = br.readLine();
            System.out.println(line);
            //输入小说名
            String name = input.next();
            //发送小说名
           /* bw.write(name);
            bw.newLine();
            bw.flush();*/
            sendUtil.sendWrite(bw,name);
            // 使用本地输入流 读取客户端的硬盘上文件
            br2 = new BufferedReader(new FileReader("D:\\PracticalTrainning3\\homework\\8.24\\book\\"+name+".txt"));
            //循环读取
            String line2 = null;
            while ((line2 = br2.readLine()) != null){//读到文件末尾
                //把读到的行发送给服务器
                sendUtil.sendWrite(bw,line2);
            }
            //小说上传完成 发送exit 告诉服务器已退出
            sendUtil.sendWrite(bw,"exit");

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            CloseUtil.close(br2,bw,br,socket);
        }
        showMenu();//回到菜单
    }

   /* public  void sendWrite(BufferedWriter bw,String str) throws IOException {
        bw.write(str);
        bw.newLine();
        bw.flush();
    }*/
}

服务器端

package readBook;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Server {
    private static List<String > nameList = new ArrayList<>();
    public static void main(String[] args) {

        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            //开启服务器
            ServerSocket serverSocket = new ServerSocket(8888);
            while (true){
                Socket accept = serverSocket.accept();
                InputStream in = accept.getInputStream();
                OutputStream out = accept.getOutputStream();
                br = new BufferedReader(new InputStreamReader(in));
                bw = new BufferedWriter(new OutputStreamWriter(out));

                //读取指令
                String flag = br.readLine();
                switch (flag){
                    case "1":
                        //处理上传
                        doUpload(accept);
                        break;
                    case "2":
                        //处理在线阅读
                        doReadOnline(accept);
                        break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void doReadOnline(Socket accept) throws IOException {
        BufferedReader br = null;
        BufferedWriter bw = null;
        br = new BufferedReader(new InputStreamReader(accept.getInputStream()));
        bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));

        File file = new File("D:\\PracticalTrainning3\\homework\\8.24\\book\\cloud");
        File[] files = file.listFiles();
        System.out.println("可阅读的书有:");
        for (File file1 : files) {
            nameList.add(file1.getName());
        }
        StringBuffer sb = new StringBuffer();
       if (nameList.isEmpty()){
           sb.append("没有书籍!");
           SendUtil.sendWrite(bw,String.valueOf(sb));
       }else {

           for (int i = 0; i < nameList.size(); i++) {
               sb.append(i+1).append(".").append(nameList.get(i));
           }
           System.out.println(sb);
           SendUtil.sendWrite(bw,sb.toString());
       }
        int num  = Integer.valueOf(br.readLine());
        String name = "";
        for (int i = 0; i < nameList.size(); i++) {
            if ((i+1) == num){//判断序号
                name = nameList.get(i);
            }
        }

        File file2 = new File(file +"\\"+name);
        BufferedReader br2 = null;
        br2 = new BufferedReader(new FileReader(file2));

        String ContentLine = "";
        int k =0;
        while ((ContentLine = br2.readLine())!= null){
            if (k==100){//读一百行
                SendUtil.sendWrite(bw,"exit");
                break;
            }
            SendUtil.sendWrite(bw,ContentLine);
            k++;
        }
       /* String content = br2.readLine();
        SendUtil.sendWrite(bw,content);

        SendUtil.sendWrite(bw,"exit");*/
        System.out.println("完成在线阅读");

        //关闭资源
        nameList.clear();
        CloseUtil.close(br2,br,bw,accept);
    }

    //处理上传
    private static void doUpload(Socket accept) throws IOException {
        BufferedReader br = null;
        BufferedWriter bw = null;

        //本地输出流
        BufferedWriter bw2 = null;

        br = new BufferedReader(new InputStreamReader(accept.getInputStream()));
        bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));

    //    System.out.println("请输入要上传的小说名");
        //发送小说名
        SendUtil.sendWrite(bw,"请输入要上传的小说名");
        String name = br.readLine();
    //    nameList.add(name);
        //创建本地输出流
        //先判断文件是否存在
        File file = new File("D:\\PracticalTrainning3\\homework\\8.24\\book\\cloud");
        if (!file.exists()){
            file.mkdirs();
        }
        bw2 = new BufferedWriter(new FileWriter(file +"\\"+ name + ".txt",true));//是否追加 不覆盖 true
        //循环接收客户端上传的内容 写入到文件
        String line = null;
        while (!((line = br.readLine()).equals("exit"))){
            SendUtil.sendWrite(bw2,line);
        }

        System.out.println("上传完成!");
        //关闭资源
        CloseUtil.close(bw2,bw,br,accept);


    }
}

封装写的工具类

package readBook;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.List;

//发送工具类
public class SendUtil {
    public static void sendWrite(BufferedWriter bw, String str) throws IOException {
        bw.write(str);
        bw.newLine();
        bw.flush();
    }

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值