给Java初学者的学习笔记

第20章 Socket通信

2019-04-13  本文已影响281人  yangsg

1. 网络传输基础知识

20世纪60年代以来,计算机网络得到了飞速增长。各大厂商为了在数据通信网络领域占据主导地 位,纷纷推出了各自的网络架构体系和标准,如IBM公司的SNA,Novell IPX/SPX协议,Apple公司的AppleTalk协议,DEC公司的DECnet,以及广泛流行的TCP/IP协议。同时,各大厂商针对自己的协议生产出了不同的硬件和软件。各个厂商的共同努力促进了网络技术的快速发展和网络设备种类的迅速增长。但由于多种协议的并存,也使网络变得越来越复杂;而且,厂商之间的网络设备大部分不能兼容,很难进行通信。

为了解决网络之间的兼容性问题,帮助各个厂商生产出可兼容的网络设备,国际标准化组织ISO于1984年提出了OSI RM(OpenSystem Interconnection Reference Model,开放系统互连参考模型)。OSI 参考模型很快成为计算机网络通信的基础模型。在设计OSI 参考模型时,遵循了以下原则:各个层之间有清晰的边界,实现特定的功能;层次的划分有利于国际标准协议的制定;层的数目应该足够多,以避免各个层功能重复。


OSI7层协议

通常OSI参考模型第一层到第三层称为底层(lower layer),又叫介质层(media layer),底层负责数据在网络中的传送,网络互连设备往往位于下三层,以硬件和软件的方式来实现。OSI参考模型的第五层到第七层称为高层(upper layer),又叫住几层(host layer),高层用于保障数据的正确传输,以软件方式来实现。


OSI7层功能

由于OSI模型和协议比较复杂,所以并没有得到广泛的应用。
而TCP/IP(transfer control protocol/internet protocol,传输控制协议/网际协议)模型因其开放性和易用性在实践中得到了广泛的应用,TCP/IP协议栈也成为互联网的主流协议。


TCP/IP协议与OSI模型对照

两台计算机间进行通讯需要以下三个条件:

2.TCP/IP通信原理和Java中处理方式

Java中的Socket和ServerSocket是基于TCP/IP协议的,其形式是“请求-响应”式,当一方发起请求后,必须在得到另一方的应答后才能继续请求。发送请求和接收响应是通过输出流和输入流完成的。

Socket类的对象负责两台计算机之间的通信
ServerSocket类负责充当服务器,监听连接请求并创建Socket连接
总体上参照下图可以理解为

Socket通信

3. 通信演示

分别创建两个项目ProjectA和ProjectB表示两台计算机


创建项目
3.1 简单通信
3.1.1 ProjectA服务器
public class StartServer {
    
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(40001);
        Socket s = ss.accept();
        
        DataInputStream dis = new DataInputStream(s.getInputStream());
        DataOutputStream dos = new DataOutputStream(s.getOutputStream());
        
        String message = dis.readUTF();
        if(message.equals("你好")) {
            dos.writeUTF("你好");
        }else {
            dos.writeUTF("Hello World!");
        }
        dos.flush();    
    }
}
3.1.2 ProjectB客户端
public class StartClient {
    
    public static void main(String[] args) throws UnknownHostException, IOException {
        Socket s = new Socket("localhost", 40001);
        
        DataInputStream dis = new DataInputStream(s.getInputStream());
        DataOutputStream dos = new DataOutputStream(s.getOutputStream());
        
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入您对服务器说的话:");
        String str = sc.nextLine();
        
        dos.writeUTF(str);
        dos.flush();
        String rec = dis.readUTF();
        System.out.println("服务器回应:"+rec);
    }
}

先启动ProjectA的服务器程序,再启动ProjectB的客户端程序
在客户端的控制台上进行如下测试


测试

可以看到服务器给出了相应的应答

3.2 多次通信

3.1小节的例子可以在服务器和客户端进行单次的消息通信,通信后两方程序执行完毕,各自退出。如果要保证两方多次通信,需要将通信代码部分设置为循环,直至客户端提出“88”再结束通信。

3.2.1 ProjectA服务器
public class StartServer {
    
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(40001);
        Socket s = ss.accept();
        
        DataInputStream dis = new DataInputStream(s.getInputStream());
        DataOutputStream dos = new DataOutputStream(s.getOutputStream());
        
        while(true) {
            String message = dis.readUTF();
            if(message.equals("你好")) {
                dos.writeUTF("你好");
            }else if(message.equals("88")){
                break;
            }else {
                dos.writeUTF("Hello World!");
            }
            dos.flush();    
        }
    }
}
3.2.2 ProjectB客户端
public class StartClient {
    
    public static void main(String[] args) throws UnknownHostException, IOException {
        Socket s = new Socket("localhost", 40001);
        
        DataInputStream dis = new DataInputStream(s.getInputStream());
        DataOutputStream dos = new DataOutputStream(s.getOutputStream());
        
        while(true) {
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入您对服务器说的话:");
            String str = sc.nextLine();
            
            dos.writeUTF(str);
            dos.flush();
            if(str.equals("88")) {
                break;
            }
            String rec = dis.readUTF();
            System.out.println("服务器回应:"+rec);
        }   
    }
}

先启动ProjectA的服务器程序,再启动ProjectB的客户端程序
在客户端的控制台上进行如下测试


测试及运行结果
3.3 多客户端通信

实际应用情况中,单个客户端在退出时是不能影响服务器的,一个服务器也会同时服务多个客户端,这样,我们需要修改服务器的处理程序为多线程方式。每个线程对象处理一个客户端的交互,将监听得到的s对象交给线程处理

3.3.1 ProjectA服务器

处理客户端交互的线程类

public class ClientThread implements Runnable{
    
    private Socket s;
    
    public ClientThread(Socket s) {
        this.s = s;
    }

    @Override
    public void run() {
        try {
            DataInputStream dis = new DataInputStream(s.getInputStream());
            DataOutputStream dos = new DataOutputStream(s.getOutputStream());
            while(true) {
                String message = dis.readUTF();
                if(message.equals("你好")) {
                    dos.writeUTF("你好");
                }else if(message.equals("88")){
                    break;
                }else {
                    dos.writeUTF("Hello World!");
                }
                dos.flush();    
            }
        } catch (IOException e) {
            System.out.println("服务器线程出错");
            return;
        }
    }
}

启动服务器

public class StartServer {
    
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(40001);
        while(true) {
            Socket s = ss.accept();
            ClientThread ct = new ClientThread(s);
            Thread t = new Thread(ct);
            t.start();
        }
    }
}
3.3.2 ProjectB客户端

与3.2小节代码一致,不再重述。

这样,在3.2小节的基础上,服务器可以同时处理多个客户端请求,且客户端关闭后不会影响服务器的运行。

上一篇下一篇

猜你喜欢

热点阅读