第20章 Socket通信

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模型对照

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

  • 协议(Protocal)数据传输协议和方式
  • IP地址(IP) 为实现网络中不同计算机之间的通信,计算机在网络上的唯一标识
  • 端口号(Port)区分一台主机的多个不同应用程序,端口号范围为0-65535,其中0-1023位为系统保留。

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

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

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

  • B创建Socket进行连接
  • A通过ServerSocket监听连接并创建Socket负责接收消息
  • B发起请求
  • A接收请求并给出回应
  • B接收A的回应,再次请求
  • A接收请求并给出回应
    ...
Socket通信

3. 通信演示

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


创建项目
  • 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小节的基础上,服务器可以同时处理多个客户端请求,且客户端关闭后不会影响服务器的运行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容