热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

网络编程原理二(网络编程socket原理)

本文主要分享【网络编程socket原理】,技术文章【网络编程原理二】为【十七ing】投稿,如果你遇到JavaEE相关问题,本文相关知识或能到你。网络编程socket原理网络编程原理二T

本文主要分享【网络编程socket原理】,技术文章【网络编程原理二】为【十七ing】投稿,如果你遇到Java EE相关问题,本文相关知识或能到你。

网络编程socket原理

网络编程原理二 TCP / IP 协议一、应用层1. 常用的现有应用层协议2. 自定义协议(1) 文本格式① xml② json (2) 二进制格式 3. 理解自定义协议(1) 基于 UDP 的 socket 写一个计算器运算① UdpCalServer② UdpCalClient 二、传输层1. 深入理解端口号2. 端口号和 PID 的区别3. 注意(1)(2) 关于传输层的 TCP / UDP 会在后两篇博客中介绍到

TCP / IP 协议

应用层
传输层
互联网层
数据链路层
物理层

1

一、应用层

应用层是我们网络编程主要针对的地方,也是程序员进行主要交互的一层。我们来看一些应用协议有哪些,以及应用层协议的两种形式。

1. 常用的现有应用层协议

DNS(域名系统)
HTTP(超文本传送协议)
FTP(文件传输协议)
Telnet(远程终端协议)

2. 自定义协议

开发之前,建立约定。开发过程中就会让客户端和服务器之间严格遵守协议约定好的格式。

自定义协议大体分成两类:

(1) 文本格式

文本格式即把请求响应当成字符串来处理,处理的基本单位是字符。

常见的文本格式有两类:xml,json
因为文本格式较为简单,当然自己也可以约定文本格式。

① xml

xml 是一种格式化组织数据的方式,它在 Java 标准库中有其对应的实现,这种格式不仅可以用于自定义协议,也可以用于网络传输。

格式如下:
整个 xml 是由 " 标签 " 构成的,此外,标签是成对出现的。

比方说: <num1> 10 </num1>

开始标签: <num1>	
结束标签: </num1>
中间值:   10
请求:								响应:

<request>							<response>
	<num1> 10 </num1>					<result> 30 </result>
	<num2> 20 </num2>				</response>
	<operator> + </operator>
</request>
② json

json 是一种键值对的方式,同样地,它在 Java 标准库中有其对应的实现。

格式如下:

键和值之间使用 [冒号] 分割,键值对之间使用 [逗号] 分割。

请求:								响应:

{
   									{
   
	num1: 10							result: 30
	num2: 20						}
	operator: "+"
}
(2) 二进制格式

把请求响应当成二进制数据处理,处理的基本单位是字节

protobuffer,thift…

3. 理解自定义协议

我们先来看一下以 【文本 + 分隔符】的方式,来自己设计一个协议。

UdpCalServer 用来描述服务器
UdpCalClient  用来描述客户端
(1) 基于 UDP 的 socket 写一个计算器运算 ① UdpCalServer
package demo1;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpCalServer {
   
    private DatagramSocket socket = null;

    public UdpCalServer(int port) throws SocketException {
   
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
   
        System.out.println("服务器启动!");

        while (true) {
   
            //2. 读取请求并解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket);
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());

            //3. 根据请求构造响应
            String response = process(request);

            //4. 构造响应并返回
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                    response.getBytes().length, requestPacket.getSocketAddress());
            socket.send(responsePacket);

            //顺便打印日志
            String log = String.format("[%s : %d], req: %s; resp: %s",
                    requestPacket.getAddress().toString(), requestPacket.getPort(),
                    request, response);
            System.out.println(log);
        }
    }

    private String process(String request) {
   
        String[] strings = request.split(";");
        if (strings.length != 3) {
   
            System.out.println("你所输入的格式不正确!");
            return null;
        }
        int num1 = Integer.parseInt(strings[0]);
        int num2 = Integer.parseInt(strings[1]);
        String key = strings[2];
        int result = 0;

        if (key.equals("+")){
   
            result = num1 + num2;
        } else if (key.equals("-")) {
   
            result = num1 - num2;
        } else if (key.equals("*")) {
   
            result = num1 * num2;
        } else {
   
            result = num1 / num2;
        }

        return result + "";
    }

    public static void main(String[] args) throws IOException {
   
        UdpCalServer server = new UdpCalServer(9090);
        server.start();
    }
}
② UdpCalClient
package demo1;

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UdpCalClient {
   
    private String serverIp;
    private int serverPort;
    private DatagramSocket socket = null;

    public UdpCalClient(String serverIp, int serverPort) throws SocketException {
   
        this.serverIp = serverIp;
        this.serverPort = serverPort;
        this.socket = new DatagramSocket();
    }

    public void start() throws IOException {
   
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入格式:num1; num2; +-*/ ");
        while (true) {
   

            System.out.println("输入计算的数和符号 ->");
            String request = scanner.nextLine();
            if (request.equals("exit")) {
   
                System.out.println("Exit!");
                return;
            }
            //1. 构造请求并发送
            DatagramPacket requestPacket = new DatagramPacket(
                    request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIp), serverPort );
            socket.send(requestPacket);

            //2. 尝试获取响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());

            //3,显示响应结果
            String log = String.format("req: %s resp: %s", request, response);
            System.out.println(log);
            System.out.println();
        }
    }

    public static void main(String[] args) throws IOException {
   
        UdpCalClient client = new UdpCalClient("127.0.0.1", 9090);
        client.start();
    }
}

通信结果:

1

在上面的例子中,我们可以看到:当计算 15+3 = 18 这个逻辑的时候,我们在服务器与客户端通信之前,就两边约定好了,将操作数放在前两位,操作符放在后一位,它们之间通过 ; 来分割。也就说,格式为:【15;3;+】
所以说,自定义协议本质上也是一种提前的约定。

这个和我们开头说的例子是一样的:网友A 和 网友B 见面,他们提前就要告知对方今天穿什么颜色的上衣和裤子,否则就算到达同一地点,也很难找到找到彼此。

二、传输层

传输层只负责端对端的数据传输,即只考虑起点和终点,不考虑中间过程。
传输层是操作系统内核实现的,因此谈到的传输层协议,一般都是指现成的一些协议,很少会涉及 " 自定制 "。

TCP / UDP 协议就是传输层中的主要协议。

1. 深入理解端口号

之前的博客提到:端口号其实是用来标识一个进程的。

一个主机中的所有进程都是通过同一个网卡来传输数据的,让每个进程分别绑定到不同的端口号,此时收到的网络数据报中就会包含一个 " 目的端口 ",它就会根据目的端口找到对应端口号的进程,从而把数据交给对应的进程。

2. 端口号和 PID 的区别

在学习线程的时候,我们了解过 PID 的概念,PID( Process Identification ),即进程标识符,操作系统里每打开一个程序都会创建一个进程 ID,即 PID. 但我们必须明确:程序在运行的时候,PID 是不会改变标识符的,但进程终止后,PID 标识符就会被系统回收,就可能会被继续分配给新运行的程序,所以说 PID 是临时的一个标志。

但是,端口号是固定不变的。

所以说:在网络编程中,我们指定一个程序 / 进程 与服务器通信,需要知道 主机的 IP地址 和 进程的端口号。而不能用 PID 来代替端口号!

举个例子:你打中国移动 10086,准备办理流量套餐,那么对面就会有人工客服接听电话,当你办理好了套餐之后,就把电话挂了。但你发现,电话套餐忘记办理了,于是就再次打电话过去,这个时候,你会发现,系统为你分配的客服和上一个客服就不再是同一个人了。那么,端口号就相当于 10086 这个固定的号码,而不同的客服就表示不同的 PID。

3. 注意 (1)

通常情况下,一个进程对应一个端口号,两个进程无法绑定同一个端口。

而比较常见的情况是:一个进程能够对应多个端口。这是通过 socket 文件和端口绑定来实现的,因为一个进程可以有多个文件,那么就可以做到让多个 socket 文件绑定到不同的端口。

让一个进程对应多个端口,实际上是为了解决【调试问题】,通常情况下,服务器会提供客户端一个【业务端口】,这个端口为客户端提供服务,如果在提供服务的时候,出现了数据问题怎么办?这时候,就可以事先让当前进程再绑定另一个【调试端口】,而这个端口就是用来定位一些问题。

(2)

端口号是一个两个字节整数,那么最多也就是 16 位,所以它的范围为 0- 65535.

知名端口号: 01023

注册端口号: 102449151

剩下的端口号叫动态端口号或私有端口号: 4915265535
关于传输层的 TCP / UDP 会在后两篇博客中介绍到

本文《网络编程原理二》版权归十七ing所有,引用网络编程原理二需遵循CC 4.0 BY-SA版权协议。


推荐阅读
author-avatar
LordHo
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有