Java网络编程——基础入门

1、进程间的通信

进程指运行中的程序,进程的任务就是执行程序中的代码。EchoPlayer类是一个独立的Java程序,它可以在任意一台安装了JDK的主机上运行:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class EchoPlayer {
    public String echo(String msg){
        return "echo:"+msg;
    }

    public void talk() throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        String msg=null;
        while ((msg=bufferedReader.readLine())!=null){
            System.out.println(echo(msg));
            if(msg.equals("bye")){
                break;
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new EchoPlayer().talk();
    }
}

运行“java EchoPlayer”命令,就启动了EchoPlayer进程,该进程执行EchoPlayer类的main()方法。下图演示了EchoPlayer进程的运行过程,它从本地控制台中获得标准输入流和标准输出流。本地控制台为用户提供了基于命令行的用户界面,用户通过控制台与EchoPlayer进程交互。
在这里插入图片描述
提示:确切地说,运行“java EchoPlayer”命令,就启动了一个Java虚拟机(Java Virtual Machine,JVM)进程,该进程执行EchoPlayer类的main()方法。为了叙述方便,把运行EchoPlayer类的main()方法的JVM进程直接称为EchoPlayer进程。

EchoPlayer类的echo(String msg)方法负责生成响应结果。如果需要把生成响应结果的功能(即echo(String msg)方法)移动到一个远程主机上,那么上面的EchoPlayer类无法满足这一需求。在这种情况下,要创建两个程序:客户程序EchoClient和服务器程序EchoServer。EchoClient程序有两个作用:

  • 与用户交互,从本地控制台获得标准输入流和标准输出流。
  • 与远程的EchoServer通信,向EchoServer发送用户输入的字符串,接收EchoServer返回的响应结果,再把响应结果写到标准输出流。

EchoServer程序负责接收EchoClient发送的字符串,然后把响应结果发送给EchoClient。下图演示了EchoClient与EchoServer的通信过程。客户机和远程服务器是通过网络连接的两台主机。客户机上运行EchoClient进程,远程服务器上运行EchoServer进程。
在这里插入图片描述
张三给李四打电话,两者顺利通话的前提条件是他们各自的电话机都连接到了电话网络上。张三和李四只需关注他们谈话的具体内容,而不必考虑如何把自己的话音传输到对方的电话机上。传输语音信息的任务是由电话网络来完成的。

同样,两个进程顺利通信的前提条件是它们所在的主机都连接到了计算机网络上。EchoClient与EchoServer只需关注它们通信的具体内容,例如EchoClient发送信息“hello”,那么EchoServer返回信息“echo:hello”。EchoClient和EchoServer都无须考虑如何把信息传输给对方。传输信息的任务是由计算机网络来完成的。

Java开发人员的任务是编写EchoClient和EchoServer程序,接下来在两台安装了JDK的主机上分别运行它们,两个进程就会有条不紊地通信。

由于进程之间的通信建立在计算机网络的基础上,Java开发人员有必要对计算机网络有基本的了解,这有助于更容易地掌握Java网络编程技术。

2、计算机网络的概念

计算机网络是现代通信技术与计算机技术相结合的产物。所谓计算机网络,指把分布在不同地理区域的计算机用通信线路互联起来的一个具有强大功能的网络系统。在计算机网络上,众多计算机可以方便地互相通信,共享硬件、软件和数据信息等资源。通俗地说,计算机网络就是通过电缆、电话线或无线通信设施等互联的计算机的集合。

网络中每台机器称为节点(Node)。大多数节点是计算机,此外,打印机、路由器、网桥、网关和哑终端等也是节点。我们通常用“节点”指代网络中的任意一个设备,用“主机”指代网络中的计算机节点。

如下图所示,人与人之间通过某种语言来交流,网络中的主机之间也通过“语言”来交流,这种语言称为网络协议,这是对网络协议的通俗解释,后面还会更深入地介绍网络协议的概念。
在这里插入图片描述
网络中的每个主机都有地址,它是用于标识主机的字节序列。字节序列越长,可以表示的地址数目就越多,这意味着可以有越多的设备连入网络。

按照计算机联网的区域大小,可以把网络分为局域网(Local Area Network,LAN)和广域网(Wide Area Network,WAN)。局域网指在一个较小地理范围内的各种计算机互联在一起的通信网络,可以包含一个或多个子网,通常局限在几千米的范围之内。例如在一个房间、一座大楼,或是在一个校园内的网络可称为局域网。广域网连接地理范围较大,常常是一个国家或是一个洲,是为了让分布较远的各局域网互联。

Internet是由许多小的网络互联成的国际性大网络,在各个小网络内部使用不同的协议,那么如何使不同的网络之间能进行信息交流呢?如下图所示,上海人讲上海方言,广东人讲广东方言,上海人与广东人用普通话沟通。
在这里插入图片描述
与此相似,不同网络之间的互联靠网络上的标准语言—TCP/IP。的互联靠网络上的标准语言—TCP/IP。如下图所示,一个网络使用协议A,另一个网络使用协议B,这两个网络通过TCP/IP进行互联。
在这里插入图片描述

3、OSI参考模型

在计算机网络产生之初,每家计算机厂商都有一套自己的网络体系结构,它们之间互不相容。为此,国际标准化组织(International Organization for Standization,ISO)在1979年建立了一个分委员会,来专门研究一种用于开放系统互联(Open System Interconnection,OSI)的体系结构,“开放”这个词意味着:一个网络系统只要遵循OSI模型,就可以和位于世界上任何地方的、也遵循OSI模型的其他网络系统连接。这个分委员会提出了OSI参考模型,它为各种异构系统互联提供了概念性的框架。

OSI参考模型把网络分为7层,分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,如下图示。每一层都使用下层提供的服务,并为上层提供服务。
在这里插入图片描述
不同主机之间的相同层称为对等层。例如主机A中的表示层和主机B中的表示层互为对等层,主机A中的会话层和主机B中的会话层互为对等层。OSI参考模型中各层的主要功能如下:

  • (1)物理层(Physical Layer):传输信息离不开物理介质,如双纽线和同轴电缆等,但物理介质并不在OSI的7层之内,有人把物理介质当作OSI的第零层。物理层的任务就是为它的上一层提供物理连接,以及规定通信节点之间的机械和电气等特性,如规定电缆和接头的类型、传送信号的电压等。在这一层上,数据作为原始的比特(bit)流被传输。本层的典型设备是集线器(Hub)。
  • (2)数据链路层(Data Link Layer):数据链路层负责在两个相邻节点间的线路上,无差错地传送以帧为单位的数据。每一帧包括一定数量的数据和一些必要的控制信息。数据链路层要负责建立、维持和释放数据链路的连接。在传送数据时,如果接收方检测到所传数据中有差错,就要通知发送方重发这一帧。本层的典型设备是交换机(Switch)。
  • (3)网络层(Network Layer):在计算机网络中进行通信的两个计算机之间可能会经过很多个数据链路,也可能还要经过很多个通信子网。网络层的任务就是选择合适的网间路由和交换节点,确保数据被及时地传送到目标主机。网络层将数据链路层提供的帧组成数据包,包中封装有网络层包头,包头中含有逻辑地址信息——源主机和目标主机的网络地址。本层的典型设备是路由器(Router)。如下图所示,主机A发送的数据先后经过节点1和节点4,最后到达主机B。相邻两个节点之间的线路被称为数据链路,比如主机A与节点1、节点1与节点4,以及节点4与主机B之间的线路。数据链路层负责数据链路上的数据传输。从主机A到主机B的整个路径被称为路由,网络层负责选择合适的路由。
    在这里插入图片描述
  • (4)传输层(Transport Layer):该层的任务是根据通信子网的特性来充分利用网络资源,为两个端系统(也就是源主机和目标主机)的会话层提供建立、维护和取消传输连接的功能,以可靠方式或者不可靠方式传输数据。所谓可靠方式,指保证把源主机发送的数据正确地送达目标主机;所谓不可靠方式,则指不保证把源主机发送的数据正确地送达目标主机,数据有可能丢失,或出错。在这一层,信息的传送单位是报文。
  • (5)会话层(Session Layer):这一层也可以被称为会晤层或对话层,在会话层及以上层次中,数据传送的单位不再另外命名,统称为报文。会话层管理进程之间的会话过程,即负责建立、管理、终止进程之间的会话。会话层还通过在数据中插入校验点来实现数据的同步。
  • (6)表示层(Presentation Layer):表示层对上层数据进行转换,以保证一个主机的应用层的数据可以被另一个主机的应用层理解。表示层的数据转换包括对数据的加密、解密、压缩、解压和格式转换等。
  • (7)应用层(Application Layer):应用层确定进程之间通信的实际用途,以满足用户实际需求。浏览Web站点、收发E-mail、上传或下载文件以及远程登入服务器等都可以看作是进程之间通信的实际用途。如下图所示,当源主机向目标主机发送数据时,在源主机方,数据先由上层向下层传递,每一层都会给上一层传递来的数据加上一个信息头(header),然后向下层发出,最后通过物理介质传输到目标主机。在目标主机方,数据再由下层向上层传递,每一层都先对数据进行处理,把信息头去掉,再向上层传输,最后到达最上层,就会还原成实际的数据。各个层加入的信息头有着不同的内容,比如网络层加入的信息头中包括源地址和目标地址信息;传输层加入的信息头中包括报文类型、源端口和目标端口、序列号和应答号等信息。在下图中,AH、PH、SH、TH、NH和DH分别表示各个层加入的信息头,数据链路层还会为数据加上信息尾DT。
    在这里插入图片描述
    在生活中,也常常采用这种方式来传输实际物品。比如张三给李四邮寄一封信,真正要传输的内容是信。为了保证信能正确到达目的地,在发送时,需要把信封装到一个信封中,上面写上发信人和收信人地址。接收方收到了被封装的信,需要拆开信封,才能得到里面的信。

OSI参考模型把网络分为多个层次,每个层次都有明确的分工,这简化了网络系统的设计过程。例如在设计应用层时,只需考虑如何创建满足用户实际需求的应用,在设计传输层时,只需考虑如何在两个主机之间传输数据,在设计网络层时,只需考虑如何在网络上找到一条发送数据的路径,即路由。

对等层之间互相通信需要遵守一定的规则,如通信的内容和通信的方式,这种规则被称为网络协议(Protocol)。值得注意的是,OSI参考模型并没有具体的实现方式,它没有在各层制定网络协议,但它为其他计算机厂商或组织制定网络协议提供了参考框架。网络的各个层次都有相应的协议,以下归纳了OSI各个层的一些典型协议,这些协议均由第三方提供:

  • ·物理层协议:EIA/TIA RS-232、EIA/TIA RS-449、V.35、RJ-45等。
  • 数据链路层协议:HDLC、PPP、IEEE 802.3/802.2等。
  • 网络层协议:IP、IPX、ICMP、IGMP、AppleTalk DDP等。
  • 传输层协议:TCP、UDP、SPX等。
  • 会话层协议:NetBIOS、RPC、NFS、AppleTalk等。
  • 表示层协议:ASCII、GIF、JPEG、MPEG等。
  • 应用层协议:TELNET、FTP、HTTP、SNMP、SMTP等。

4、TCP/IP参考模型和TCP/IP

国际标准化组织ISO制订的OSI参考模型提出了网络分层的思想,这种思想对网络的发展具有重要的指导意义。但由于OSI参考模型过于庞大和复杂,所以它难以被投入实际运用中。与OSI参考模型相似的TCP/IP参考模型吸取了网络分层的思想,但是对网络的层次做了简化,并且在网络各层(除主机-网络层外)都提供了完善的协议,这些协议构成了TCP/IP集,简称TCP/IP。TCP/IP是目前最流行的商业化协议,它是当前的工业标准或“事实标准”,TCP/IP主要用于广域网,在一些局域网中也有运用。

TCP/IP参考模型是美国国防部高级研究计划局计算机网(Advanced Research Project Agency Network,ARPANET)以及后来的Internet使用的参考模型。ARPANET是由美国国防部(U.S.Department of Defense,DoD)赞助的研究网络。最初,它只连接了美国境内的四所大学。在随后的几年中,它通过租用的电话线连接了数百所大学和政府部门。最终,ARPANET发展成为全球规模最大的互联网络——Internet。最初的ARPANET则于1990年永久关闭。下图把TCP/IP参考模型和OSI参考模型进行了对比。
在这里插入图片描述
TCP/IP参考模型分为4个层次:应用层、传输层、网络互联层和主机-网络层。每一层都有相应的协议。确切地说,TCP/IP应该被称为TCP/IP集,它是TCP/IP参考模型的除主机-网络层以外的其他三层的协议的集合,而IP和TCP则是协议集的最核心的两个协议。下表列出了各层的主要协议,其中主机-网络层的协议是由第三方提供的。
在这里插入图片描述
在TCP/IP参考模型中,去掉了OSI参考模型中的会话层和表示层,这两层的功能被合并到应用层,同时将OSI参考模型中的数据链路层和物理层合并到主机-网络层。下面分别介绍各层的主要功能。

  • (1)主机-网络层:实际上TCP/IP参考模型没有真正提供这一层的实现,也没有提供协议。它只是要求第三方实现的主机-网络层能够为上层——网络互联层提供一个访问接口,使得网络互联层能利用主机-网络层来传递IP数据包。美国电气及电子工程师学会(Institute of Electrical and Electronics Engineer,IEEE)制定了IEEE802.3和IEEE802.4协议集,它们位于OSI参考模型的物理层和数据链路层,相当于位于TCP/IP参考模型的主机-网络层。采用IEEE802.3协议集的网络被称为以太网,采用IEEE802.4协议集的网被称为令牌环网。以太网和令牌环网都向网络互联层提供了访问接口。
  • (2)网络互联层:网络互联层是整个参考模型的核心。它的功能是把IP数据包发送到目标主机。为了尽快地发送数据,把原始数据分为多个数据包,然后沿不同的路径同时传递。如下图所示,由主机A发出的原始数据被分为3个数据包,然后沿不同的路径到达主机B,可谓殊途同归。数据包到达的先后顺序和发送的先后顺序可能不同,这就需要上层——传输层对数据包重新排序,还原为原始数据。
    在这里插入图片描述
    网络互联层具备连接异构网的功能。下图展示了其连接以太网和令牌环网的方式。以太网和令牌环网是不同类型的网,两者有不同的网络拓扑结构。以太网和令牌环网都向网络互联层提供了统一的访问接口,访问接口向网络互联层隐藏了下层网络的差异,使得两个网络之间可以顺利传递数据包。
    在这里插入图片描述
    网络互联层采用的是IP(Internet Protocol),它规定了数据包的格式,并且规定了为数据包寻找路由的流程。
  • (3)传输层:传输层的功能是使源主机和目标主机上的进程可以进行会话。在传输层定义了两种服务质量不同的协议,即传输控制协议(Transmission Control Protocol,TCP)和用户数据报协议(User Datagram Protocol,UDP)。TCP是一种面向连接的、可靠的协议。它将源主机发出的字节流无差错地发送给互联网上的目标主机。在发送端,TCP负责把从上层传送下来的数据分成报文段并传递给下层。在接收端,TCP负责把收到的报文进行重组后递交给上层。TCP还要处理端到端的流量控制,以避免接收速度缓慢的接收方没有足够的缓冲区来接收大量数据。应用层的许多协议,如HTTP(Hyper Text Transfer Protocol)、FTP(File Transfer Protocol)和TELNET协议等都建立在TCP基础上。UDP是一个不可靠的、无连接协议,主要适用于不需要对报文进行排序和流量控制的场合。UDP不能保证数据报的接收顺序同发送顺序相同,甚至不能保证它们是否全部到达目标主机。应用层的一些协议,如SNMP和DNS协议就建立在UDP基础上。如果要求可靠的传输数据,则应该避免使用UDP,而要使用TCP。
  • (4)应用层:TCP/IP模型将OSI参考模型中的会话层和表示层的功能合并到应用层实现。针对各种各样的网络应用,应用层引入了许多协议。基于TCP的应用层协议主要包括以下几类:
    FTP: 文件传输协议,允许在网络上传输文件。
    Telnet: 虚拟终端协议,允许从主机A登入远程主机B,使得主机A充当远程主机B的虚拟终端。
    HTTP: 超文本传输协议,允许在网络上传送超文本。
    HTTPS(Secure Hypertext Transfer Protocol):安全超文本传输协议,允许在网络上安全地传输超文本,网上传输的是经过加密的数据,到达目的地后再对数据解密。
    POP3(Post Office Protocol-Version 3):邮局协议-版本3,允许用户在客户程序中访问在远程服务器上的电子邮件。
    IMAP4(Internet Message Access Protocol Version 4):Internet消息访问协议-版本4,允许用户访问和操纵远程服务器上的邮件和邮件夹。IMAP4改进了POP3的不足,用户可以通过浏览信件头来决定是不是要下载此邮件,还可以在服务器上创建或更改文件夹或邮箱,删除邮件或检索邮件的特定部分。在POP3中,邮件是被保存在服务器上的,当用户阅读邮件时,所有内容都会被立刻下载到用户的机器上。IMAP4服务器可以被看作是一个远程文件服务器,而POP3服务器可以被看作是一个存储转发服务器。
    SMTP(Simple Mail Transfer Protocol):简单邮件传送协议,是发送电子邮件的协议。

4.1、IP

IP网络(即在网络层采用IP的网)中的每台主机都有唯一的IP地址,IP地址用于标识网络中的每个主机。IP地址分为IPv4和IPv6。

  • (1)IPv4:用32位的二进制序列来表示主机地址。为了便于在上层应用中方便地表示IP地址,可以把32位的二进制序列分为4个单元,每个单元占8位,然后用十进制整数来表示每个单元,这些十进制整数的取值范围是0~255。如某一台主机的IP地址可为192.166.3.4。相对于IPv6,IPv4是更早期出现的IP地址形式,但现在仍然使用广泛。
  • (2)IPv6:用128位的二进制序列来表示主机地址,大大扩充了可用地址的数目,IPv6是新一代的互联网络IP地址标准。IPv6的128位地址通常分成8段,每段为4个十六进制数。例如:
    在这里插入图片描述
    以上地址比较长,不易于阅读和书写。零压缩法可以用来缩减其长度。如果有一个段或几个连续段的值都是0,那么这些0就可以简单地以“::”来表示。上述地址可写成:
    在这里插入图片描述
    值得注意的是,这种零压缩法只能用一次。例如上例中的“ABAA”后面的“0000”就不能再次简化。这种限制的目的是为了能准确还原被压缩的0,不然就无法确定每个“::”代表多少个0。另外,如果一个段中包含4个0,那么可以用一个0来表示。以下3个IPv6地址是等价的:
    在这里插入图片描述
    在IPv6地址中可以嵌入IPv4地址,用IPv6和IPv4的混合体来表示。例如:
    在这里插入图片描述
    在以上地址中,“::FFFF”采用IPv6形式,“192.168.89.9”采用IPv4形式。以上地址等价于以下的IPv6地址:
    在这里插入图片描述

1、IP地址的组成

下面以IPv4为例,介绍IP地址的组成。IP地址由两部分组成:IP网址和IP主机地址。IP网址表示网络的地址,IP主机地址表示网络中的主机的地址。网络掩码用来确定IP地址中哪部分是网址,哪部分是主机地址。

网络掩码的形式与IP地址相同,但有一定的限制。在网络掩码的二进制序列中,前面部分都为1,后面部分都为0。假定IP地址192.166.3.4的网络掩码为255.255.255.0。这个网络掩码的二进制序列为11111111.11111111.11111111.00000000。把网络掩码与IP地址进行二进制与操作,得到的结果就是IP网址。因此,IP地址192.166.3.4的网址为192.166.3.0。如果把网络掩码设为255.255.0.0,那么IP网址为192.166.0.0。

提示:在Internet上,每个主机都必须有全球范围内唯一的IP地址。国际机构NIC(Internet Network Information Center)统一负责全球地址的规划和管理,与此同时,InterNIC、APNIC、RIPE和CNNIC等机构具体负责美国及全球其他地区的IP地址分配。中国地区的IP地址分配由CNNIC机构负责。

下图展示了两个互联的网络的配置,从该图可以看出,每个网络都有IP网址,两个网络之间用路由器连接。
在这里插入图片描述

2、子网划分

一个公司可能拥有一个网址和多个主机。例如,如果网址为192.166.0.0,则可以有216(即65536)个主机加入网中。为了更好地管理网络,提高网络性能和安全性,可以把网络划分为多个子网。子网可包括某地理位置内(如某大楼或相同局域网中)的所有主机。例如对于网址为“192.166.0.0”的网络,可从整个网络中分出3个子网,这3个子网的网址分别为:192.166.1.0,192.166.2.0和192.166.3.0,这些子网的掩码都为255.255.255.0。

3、发送数据包的过程

IP是面向包的协议,即数据被分成若干小数据包,然后分别传输它们。IP网络上的主机只能直接向本地网上的其他主机(也就是具有相同IP网址的主机)发送数据包。主机实际上有两个不同性质的地址:物理地址和IP地址。物理地址是由主机上的网卡来标识的,物理地址才是主机的真实地址。如下图所示,主机A向同一个网络上的另一个主机B发包时,会通过地址解析协议(Address Resolution Protocol,ARP)获得对方的物理地址,然后把包发给对方。ARP的运行机制为主机A在网络上广播一个ARP消息:“要寻找地址为192.166.3.5的主机”,接着,具有这个IP地址的主机B就会做出响应,把自身的物理地址告诉主机A。
在这里插入图片描述
当主机A向另一个网络上的主机B发送包时,主机A利用ARP找到本地网络上的路由器的物理地址,把包转发给它。路由器会按照如下步骤处理数据包:

  • (1)如果数据包的生命周期已到,则该数据包被抛弃。
  • (2)搜索路由表,优先搜索路由表中的主机,如果能找到具有目标IP地址的主机,则将数据包发送给该主机。
  • (3)如果匹配主机失败,则继续搜索路由表,匹配同子网的路由器,如果找到匹配的路由器,则将数据包转发给该路由器。
  • (4)如果匹配同子网的路由器失败,则继续搜索路由表,匹配同网络的路由器,如果找到匹配的路由器,则将数据包转发给该路由器。
  • (5)如果以上匹配操作都失败,就搜索默认路由,如果默认路由存在,则按照默认路由发送数据包,否则丢弃数据包。

从以上路由器的处理步骤可以看出,IP并不保证一定把数据包送达目标主机,在发送过程中,会因为数据包结束生命周期,或者找不到路由而丢弃数据包。

4、域名

虽然IP地址能够唯一标识网络上的主机,但IP地址是数字型的,用户记忆数字型的IP地址很不方便,于是人们又发明了另一种字符型的地址,即所谓的域名(Domain Name)。域名地址具有易于理解的字面含义,便于记忆。IP地址和域名一一对应。例如JavaThinker网站的域名为www.javathinker.net,对应的IP地址为:43.247.68.17。

域名是从右至左来表述其意义的,最右边的部分为顶层域,最左边的则是这台主机的机器名称。域名一般可表示为:主机机器名.单位名.网络名.顶层域名。如:mail.xyz.edu.cn,这里的mail是xyz学校的一个主机的机器名,xyz代表一个学校的名字,edu代表中国教育科研网,cn代表中国,顶层域一般是网络机构或所在国家地区的名称缩写。

DNS(Domain Name System)协议采用DNS服务器提供把域名转换为IP地址的服务。DNS服务器分布在网络的各个地方,它们存放了域名与IP地址的映射信息。用户需要访问网络上某个主机时,只需提供主机直观的域名,DNS协议首先请求地理位置比较近的DNS服务器进行域名到IP地址的转换,如果在该服务器中不存在此域名信息,那么DNS协议再让远方的DNS服务器提供服务。

5、URL(统一资源定位器)

URL(Uniform Resource Locator),即统一资源定位器。它是专为标识网络上资源位置而设的一种编址方式,大家熟悉的网页地址就属于URL。URL一般由3部分组成:
应用层协议://主机IP地址或域名/资源所在路径/文件名

例如JavaThinker网站提供的JDK安装软件包的URL为:
在这里插入图片描述
其中“http”指超文本传输协议,“www.javathinker.net”是Web服务器的域名,“software”是文件所在路径,“jdk8.exe”才是相应的文件。

在URL中,常见的应用层协议还包括ftp和file等,比如:
在这里插入图片描述
以上file协议用于访问本地计算机上的文件,使用这种协议的URL以“file:///”开头。

4.2、TCP以及端口

IP在发送数据包的途中会遇到各种状况,例如可能路由器突然崩溃,使数据包丢失。再例如可能前面的数据包沿低速链路移动,而后面的数据包沿高速链路移动而超过前面的包,最后使得数据包的顺序混乱。

TCP使两台主机上的进程顺利通信,不必担心数据包丢失或顺序混乱。TCP跟踪数据包顺序,并且在数据包顺序混乱时按正确顺序对其进行重组。如果数据包丢失,则TCP会请求源主机重新发送。

如下图所示,两台主机上都会运行许多进程。当主机A上的进程A1向主机B上的进程B1发送数据时,IP根据主机B的IP地址,把进程A1发送的数据送达主机B。接下来,TCP需要决定把数据发送到主机B中的哪个进程。TCP采用端口来区分进程。端口不是物理设备,而是用于标识进程的逻辑地址,更确切地说,是用于标识TCP连接端点的逻辑地址。当两个进程进行一次通信时,就意味着建立了一个TCP连接,TCP连接的两个端点用端口来标识。在下图中,进程A1与进程B1之间建立了一个TCP连接,进程B1的端口为80,因此进程B1的地址为主机B:80。进程A1的端口为1000,因此进程A1的地址为主机A:1000。每个进程都有了唯一的地址,TCP就能保证把数据顺利送达特定的进程。
在这里插入图片描述
提示:在客户/服务器模型中,客户进程可能会与服务器进程同时建立多个TCP连接,在客户端,每一个TCP连接都被分配一个端口。参见下图:
在这里插入图片描述
端口号的范围为0到65535,其中0到1023的端口号一般被固定分配给一些服务。比如21端口被分配给FTP服务,25端口被分配给SMTP(简单邮件传输)服务,80端口被分配给HTTP(超级文本传输)服务,135端口被分配给RPC(远程过程调用)服务等等。

从1024到65535的端口号供用户自定义的服务使用。比如假定范例中的EchoServer服务使用8000端口。当EchoServer程序运行时,就会占用8000端口,当程序运行结束时,就会释放所占用的端口。

客户进程的端口一般由所在主机的操作系统动态分配,当客户进程要求与一个服务器进程进行TCP连接时,操作系统会为客户进程随机地分配一个还未被占用的端口,当客户进程与服务器进程断开连接时,这个端口就被释放。

此外还要指出的是,TCP和UDP都用端口来标识进程。在一个主机中,TCP端口与UDP端口的取值范围是各自独立的,允许存在取值相同的TCP端口与UDP端口。如下图所示,在主机A中,进程A1占用FTP端口1000,进程A2占用UDP端口1000,这是被允许的。
在这里插入图片描述

4.3、RFC简介

TCP/IP是以RFC(Request For Comment)文档的形式发布的。RFC指描述互联网相关技术规范的文档。

RFC由个人编写,这些人自愿编写某一新协议或规范的提议草案,并提交给Internet工程任务组织(The Internet Engineering Task Force,IETF)。IETF负责审阅和发布这些被统称为RFC的文档,每个文档都有一个RFC编号,并且处于以下六种类型之一:

  • 标准协议:Internet 的官方标准协议。
  • 标准协议草案:正在被积极地考虑和审阅以便成为标准协议。
  • 标准协议提议:将来可能变成标准协议。
  • 实验性协议:为实验目的而设计的协议。实验性协议不投入实际运用。
  • 报告性协议:由其他标准组织开发的协议。
  • 历史性协议:已经过时的协议,被其他协议代替。

在FRC的官方网站上已经发布了8000多份RFC文档。下表列出了与TCP/IP 协议相关的RFC文档编号。
在这里插入图片描述
在FRC的官方网站上输入网址:http://www.ietf.org/rfc/rfcXXXX.txt,就能查看相关的FRC文档,这里的XXXX表示文档编号。

RFC文档一旦被正式发布,其编号和内容就不允许改变。如果需要更新RFC文档,则会对更新后的RFC文档赋予新的编号,再将它发布。例如HTTP1.0协议对应的RFC文档编号为RFC1945,它的升级版本HTTP1.1协议对应的RFC文档编号为RFC2068。

4.4、客户/服务器通信模式

TCP/UDP推动了客户/服务器通信模式的广泛运用。在通信的两个进程中,一个进程为客户进程,另一个进程为服务器进程。客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求。如下图所示,通常,一个服务器进程会同时为多个客户进程服务,图中服务器进程B1同时为客户进程A1、A2和B2提供服务。以下伪代码演示了服务器进程的大致工作流程:
在这里插入图片描述
服务器进程可以提供各种各样的服务,例上面提到的EchoServer提供的服务为根据EchoClient发出的字符串XXX,返回字符串“echo:XXX”。除了像EchoServer这样的由用户自定义的服务,网络上还有许多众所周知的通用服务,最典型的是HTTP服务。网络应用层的协议规定了客户程序与这些通用服务器程序的通信细节,例如HTTP规定了HTTP客户程序发出的请求的格式,还规定了HTTP服务器程序发回的响应的格式。

在现实生活中,有些重要的服务机构的电话是固定的,这有助于人们方便地记住电话和获得服务,比如众所周知的电话110、120和119分别是报警、急救和火警电话。同样,在网络上有些通用的服务有着固定的端口,下表对常见的服务以及相应的协议和端口做了介绍。
在这里插入图片描述

5、用Java编写客户/服务器程序

Java网络程序都建立在TCP/IP基础上,致力于实现应用层。传输层向应用层提供了套接字Socket接口,Socket封装了下层的数据传输细节,应用层的程序通过Socket来建立与远程主机的TCP连接以及进行数据传输。

站在应用层的角度,两个进程之间的一次通信过程从建立TCP连接开始,接着交换数据,到断开连接结束。套接字可以被看作是通信线路两端的收发器,进程通过套接字来收发数据,如下图所示:
在这里插入图片描述
在上图中,如果把进程A1和程B1比作两个人,那么图中的两个Socket就像两个人各自持有的电话机的话筒,只要拨通了电话,两个人就能通过各自的话筒进行通话。

在Java中,有3种套接字类:java.net.Socket、java.net.ServerSocket和DatagramSocket。其中Socket和ServerSocket类建立在TCP基础上,DatagramSocket类建立在UDP基础上。Java网络程序都采用客户/服务通信模式。

5.1、 创建EchoServer

服务器程序通过一直监听端口,来接收客户程序的连接请求。在服务器程序中,需要先创建一个ServerSocket对象,在构造方法中指定监听的端口:
在这里插入图片描述
ServerSocket的构造方法负责在操作系统中把当前进程注册为服务器进程。服务器程序接下来调用ServerSocket对象的accept()方法,该方法一直监听端口,等待客户的连接请求,如果接收到一个连接请求,accept()方法就会返回一个Socket对象,这个Socket对象与客户端的Socket对象形成了一条通信线路:
在这里插入图片描述
Socket类提供了getInputStream()方法和getOutputStream()方法,分别返回输入流InputStream对象和输出流OutputStream对象。程序只需向输出流写数据,就能向对方发送数据;只需从输入流读数据,就能接收来自对方的数据。下图演示了服务器与客户利用ServerSocket和Socket来通信的过程。
在这里插入图片描述
与普通I/O流一样,Socket的输入流和输出流也可以用过滤流来装饰。在以下代码中,先获得输出流,然后用PrintWriter装饰它,PrintWriter的println()方法能够写一行数据;以下代码接着获得输入流,然后用BufferedReader装饰它,BufferedReader的readLine()方法能够读入一行数据。
在这里插入图片描述
下面是EchoServer的源程序:

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

/**
 * @title EchoServer
 * @description 测试
 * @author: yangyongbing
 * @date: 2023/12/5 10:04
 */
public class EchoServer {
    private int port=8080;
    private ServerSocket serverSocket;

    public EchoServer() throws IOException {
        serverSocket = new ServerSocket(port);
        System.out.println("服务器启动");
    }

    public String echo(String msg){
        return "echo:"+msg;
    }

    private PrintWriter getWriter(Socket socket) throws IOException {
        OutputStream socketOutputStream = socket.getOutputStream();
        return new PrintWriter(socketOutputStream,true);
    }

    private BufferedReader getReader(Socket socket) throws IOException {
        InputStream socketInputStream = socket.getInputStream();
        return new BufferedReader(new InputStreamReader(socketInputStream));
    }

    public void service(){
        while (true){
            Socket socket=null;
            try {
                // 等待客户端连接
                socket=serverSocket.accept();
                System.out.println("New connection accepted "+socket.getInetAddress()+":"+socket.getPort());
                BufferedReader bufferedReader = getReader(socket);
                PrintWriter printWriter = getWriter(socket);
                String msg=null;
                while ((msg=bufferedReader.readLine())!=null){
                    System.out.println(msg);
                    printWriter.println(echo(msg));
                    // 如果客户端发送的消息为"bye",则通信结束
                    if(msg.equals("bye")){
                        break;
                    }
                }
            }catch (IOException e){
                e.printStackTrace();
            }finally {
                if(socket!=null){
                    // 断开连接
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new EchoServer().service();
    }
}

EchoServer类最主要的方法为service()方法,它不断等待客户的连接请求,当serverSocket.accept()方法返回一个Socket对象,就意味着与一个客户建立了连接。接下来从Socket对象中得到输出流和输入流,并且分别用PrintWriter和BufferedReader来装饰它们。然后不断调用BufferedReader的readLine()方法读取客户发来的字符串XXX,再调用PrintWriter的println()方法向客户返回字符串echo:XXX。当客户发来的字符串为“bye”时,就会结束与客户的通信,调用socket.close()方法断开连接。

5.2、创建EchoClient

在EchoClient程序中,为了与EchoServer通信,需要先创建一个Socket对象:
在这里插入图片描述
在以上Socket的构造方法中,参数host表示EchoServer进程所在的主机的名字,参数port表示EchoServer进程监听的端口。当参数host的取值为“localhost”时,表示EchoClient与EchoServer进程运行在同一个主机上。如果Socket对象被成功创建,就表示建立了EchoClient与EchoServer之间的连接。接下来,EchoClient从Socket对象中得到了输出流和输入流,就能与EchoServer交换数据。

下面为EchoClient的源程序:

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

/**
 * @title EchoClient
 * @description 测试
 * @author: yangyongbing
 * @date: 2023/12/5 10:32
 */
public class EchoClient {
    private String host="localhost";
    private Socket socket;
    private  int port=8080;

    public EchoClient() throws IOException {
        socket = new Socket(host,port);
    }

    private PrintWriter getWriter(Socket socket) throws IOException {
        OutputStream socketOutputStream = socket.getOutputStream();
        return new PrintWriter(socketOutputStream,true);
    }

    private BufferedReader getReader(Socket socket) throws IOException {
        InputStream socketInputStream = socket.getInputStream();
        return new BufferedReader(new InputStreamReader(socketInputStream));
    }

    public void talk() throws IOException {
        try {
            BufferedReader bufferedReader = getReader(socket);
            PrintWriter printWriter = getWriter(socket);
            BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
            String msg=null;
            while ((msg=localReader.readLine())!=null){
                printWriter.println(msg);
                System.out.println(bufferedReader.readLine());
                if(msg.equals("bye")){
                    break;
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                socket.close();
            }catch (IOException e){
                e.printStackTrace();
            }

        }
    }

    public static void main(String[] args) throws IOException {
        new EchoClient().talk();
    }
}

在EchoClient类中,最主要的方法为talk()方法。该方法不断读取用户从控制台输入的字符串,然后把它发送给EchoServer,再把EchoServer返回的字符串打印到控制台。如果用户输入的字符串为“bye”,就会结束与EchoServer的通信,调用socket.close()方法断开连接。

在运行范例时,需要打开两个命令行界面,先在一个命令行界面中运行“java EchoServer”命令,再在另一个命令行界面中运行“java EchoClient”命令。下图显示了运行这两个程序的命令行界面。在EchoClient控制台,用户输入字符串“hi”,程序就会输出“echo:hi”。
在这里插入图片描述

提示:如果希望在一个命令行控制台中同时运行EchoServer和EchoClient程序,那么可以先运行“start java EchoServer”命令,再运行“java EchoClient”命令。“start java EchoServer”命令中“start”的作用是打开一个新的命令行控制台,然后在该控制台中运行“java EchoServer”命令。
在这里插入图片描述

在EchoServer程序的service()方法中,每当serverSocket.accept()方法返回一个Socket对象,都表示建立了与一个客户的连接,这个Socket对象中包含了客户的地址和端口信息,只需调用Socket对象的getInetAddress()和getPort()方法就能分别获得这些信息:
在这里插入图片描述
从上图可以看出,EchoServer的控制台显示EchoClient的IP地址为127.0.0.1,端口为9001。127.0.0.1是本地主机的IP地址,表明EchoClient与EchoServer在同一个主机上。EchoClient作为客户程序,它的端口是由操作系统随机产生的。每当客户程序创建一个Socket对象,操作系统都会为客户分配一个端口。假定在客户程序中先后创建了两个Socket对象,这就意味着客户与服务器之间同时建立了两个连接:
在这里插入图片描述
操作系统为客户的每个连接分配一个唯一的端口,如上图所示。

在客户进程中,Socket对象包含了本地以及对方服务器进程的地址和端口信息,在服务器进程中,Socket对象也包含了本地以及对方客户进程的地址和端口信息,Socket类的以下方法用于获取这些信息:

  • getInetAddress():获得远程被连接进程的IP地址。
  • getPort():获得远程被连接进程的端口。
  • getLocalAddress():获得本地的IP地址。
  • getLocalPort():获得本地的端口。

客户进程允许建立多个连接,每个连接都有唯一的端口。在图上图中,客户进程占用两个端口:9001和10028。在编写网络程序时,一般只需要显式地为服务器程序中的ServerSocket设置端口,而不必考虑客户程序所用的端口。

5、总结

简单地理解,计算机网络的任务就是传输数据。为了完成这一复杂的任务,国际标准化组织ISO提供了OSI参考模型,这种模型把互联网络分为七层,分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。每个层有明确的分工,并且在层与层之间,下层为上层提供服务。这种分层的思想简化了网络系统的设计过程。例如在设计应用层时,只需考虑如何创建满足用户实际需求的应用,在设计传输层时,只需考虑如何在两个主机之间传输数据,在设计网络层时,只需考虑如何在网络上找到一条发送数据的路径,即路由。

由于OSI参考模型过于庞大和复杂,使它难以投入实际运用中。与OSI参考模型相似的TCP/IP参考模型吸取了网络分层的思想,但是对网络的层次做了简化,并且在网络各层(除主机-网络层外)都提供了完善的协议,这些协议构成了TCP/IP集,简称TCP/IP。TCP/IP参考模型分为四层,分别是应用层、传输层、网络互联层和主机-网络层。每一层都有相应的协议,IP和TCP是协议集中最核心的两个协议。

IP位于网络互联层,用IP地址来标识网络上的各个主机,IP把数据分为若干数据包,然后为这些数据包确定合适的路由。路由指把数据包从源主机发送到目标主机的路径。

建立在TCP/IP基础上的网络程序一般都采用客户/服务器通信模式。服务器程序提供服务,客户程序请求获得服务。服务器程序一般昼夜运行,时刻等待客户的请求并及时做出响应。

Java网络程序致力于实现应用层。传输层向应用层提供了套接字Socket接口,Socket封装了下层的数据传输细节,应用层的程序通过Socket来建立与远程主机的TCP连接以及进行数据传输。在Java中,有3种套接字类:java.net.Socket、java.net.ServerSocket和DatagramSocket。其中Socket和ServerSocket类建立在TCP基础上;DatagramSocket类建立在UDP基础上。

  • 33
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【完整课程列表】 资源汇总下载地址:https://download.csdn.net/download/qq_27595745/55418618 Java SE编程入门教程 collection与collections(共7页).pptx Java SE编程入门教程 java GC(共6页).pptx Java SE编程入门教程 java instanceof(共3页).pptx Java SE编程入门教程 java IO(共28页).pptx Java SE编程入门教程 java Math(共11页).pptx Java SE编程入门教程 java object(共9页).pptx Java SE编程入门教程 java static final(共24页).pptx Java SE编程入门教程 java this和super(共10页).pptx Java SE编程入门教程 java 常用API(共22页).pptx Java SE编程入门教程 javaGUI编程快速入门(1)(共82页).pptx Java SE编程入门教程 java包装器(共9页).pptx Java SE编程入门教程 java反射机制(共16页).pptx Java SE编程入门教程 java泛型(共11页).pptx Java SE编程入门教程 java封装继承多态(共56页).pptx Java SE编程入门教程 java集合(共38页).pptx Java SE编程入门教程 java接口(共21页).pptx Java SE编程入门教程 java类和对象方法以及包的使用(共56页).pptx Java SE编程入门教程 java类和对象方法以及包的使用(共62页).pptx Java SE编程入门教程 java判断循环(共79页).ppt Java SE编程入门教程 java入门以及变量debug(共63页).pptx Java SE编程入门教程 java设计模式(共17页).pptx Java SE编程入门教程 java数组(共33页).pptx Java SE编程入门教程 java网络编程(共29页).pptx Java SE编程入门教程 java线程(共61页).pptx Java SE编程入门教程 java序列化(共14页).pptx Java SE编程入门教程 java异常(共57页).pptx Java SE编程入门教程 java正则(共8页).pptx Java SE编程入门教程 properties(共3页).pptx Java SE编程入门教程 String字符串(共27页).pptx Java 位运算.docx java正则表达式例子大全 共10页.doc JAVA正则表达式实例教程 共17页.docx JAVA正则表达式实例教程 共17页.pdf 递归算法.docx 堆和栈.wps
您好!感谢您的提问。 以下是一个简单的 Android 计数器的基础编码示例: 1. 在 activity_main.xml 文件中添加以下代码: ``` <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/counterTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0" android:textSize="50dp" android:layout_gravity="center_horizontal"/> <Button android:id="@+id/incrementButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Increment" android:layout_gravity="center_horizontal" android:onClick="incrementCounter"/> <Button android:id="@+id/resetButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Reset" android:layout_gravity="center_horizontal" android:onClick="resetCounter"/> </LinearLayout> ``` 2. 在 MainActivity.java 文件中添加以下代码: ``` public class MainActivity extends AppCompatActivity { private int counter = 0; private TextView counterTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); counterTextView = findViewById(R.id.counterTextView); } public void incrementCounter(View view) { counter++; counterTextView.setText(Integer.toString(counter)); } public void resetCounter(View view) { counter = 0; counterTextView.setText(Integer.toString(counter)); } } ``` 3. 运行应用程序,您将看到一个显示当前计数器值的 TextView 和两个按钮:Increment 和 Reset。单击 Increment 按钮将增加计数器值,单击 Reset 按钮将将计数器值重置为零。 希望这可以帮助您入门 Android 编程!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值