java网络编程

1、计算机网络基础

OSI参考模型(开放系统互连参考模型):

这里写图片描述

物理层:物理层处于OSI的最底层,是整个开放系统的基础。物理层涉及通信信道上传输的原始比特流(bits),它的功能主要是为数据端设备提供传送数据的通路以及传输数据

数据链路层:数据链路层的主要任务是实现计算机网络中相邻节点之间的可靠传输,把原始的、有差错的物理传输线路加上数据链路协议以后,构成逻辑上可靠的数据链路。需要完成的功能有链路管理、成帧、差错控制以及流量控制等。其中成帧是对物理层的原始比特流进行界定,数据链路层也能够对帧的丢失进行处理。

网络层:网络层涉及源主机节点到目的主机节点之间可靠的网络传输,它需要完成的功能主要包括路由选择、网络寻址、流量控制、拥塞控制、网络互连等。

传输层:传输层起着承上启下的作用,涉及源端节点到目的端节点之间可靠的信息传输。传输层需要解决跨越网络连接的建立和释放,对底层不可靠的网络,建立连接时需要三次握手,释放连接时需要四次挥手。

会话层和表示层:会话层的主要功能是负责应用程序之间建立、维持和中断会话,同时也提供对设备和结点之间的会话控制,协调系统和服务之间的交流,并通过提供单工、半双工和全双工3种不同的通信方式,使系统和服务之间有序地进行通信。
表示层关心所传输数据信息的格式定义,其主要功能是把应用层提供的信息变换为能够共同理解的形式,提供字符代码、数据格式、控制信息格式、加密等的统一表示。

应用层
应用层为OSI的最高层,是直接为应用进程提供服务的。其作用是在实现多个系统应用进程相互通信的同时,完成一系列业务处理所需的服务。

TCP/IP参考模型(传输控制协议/因特网互联协议)

TCP/IP,即Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,是Internet最基本的协议、Internet国际互联网络的基础。
TCP/IP协议定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。TCP/IP参考模型采用4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求,这4个层次分别是:网络接口层、互联网层(IP层)、传输层(TCP层)、应用层。

这里写图片描述

网络接口层(数据链路层):网络接口层对应着OSI物理层和数据链路层。

互联网层(IP层)(网络层):互联网层是整个TCP/IP协议栈的核心。它的功能是把分组发往目标网络或主机。同时,为了尽快地发送分组,可能需要沿不同的路径同时进行分组传递。因此,分组到达的顺序和发送的顺序可能不同,这就需要上层必须对分组进行排序。互联网层除了需要完成路由的功能外,也可以完成将不同类型的网络(异构网)互连的任务。除此之外,互联网层还需要完成拥塞控制的功能。

传输层(TCP层):TCP层负责在应用进程之间建立端到端的连接和可靠通信,它只存在与端节点中。TCP层涉及两个协议,TCP和UDP。其中,TCP协议提供面向连接的服务,提供按字节流的有序、可靠传输,可以实现连接管理、差错控制、流量控制、拥塞控制等。UDP协议提供无连接的服务,用于不需要或无法实现面向连接的网络应用中。

应用层:应用层为Internet中的各种网络应用提供服务

这里写图片描述

TCP报文段格式:TCP报文段包括协议首部和数据两部分,协议首部的固定部分有20个字节,首部的固定部分后面是选项部分。
这里写图片描述
TCP报文段
下面是报文段首部各个字段的含义。

1.源端口号以及目的端口号,各占2个字节,端口是传输层和应用层的服务接口,用于寻找发送端和接收端的进程,一般来讲,通过端口号和IP地址,可以唯一确定一个TCP连接,在网络编程中,通常被称为一个socket接口。

2.序号,占4字节,用来标识从TCP发送端向TCP接收端发送的数据字节流。

3.确认序号,占4字节,包含发送确认的一端所期望收到的下一个序号,因此,确认序号应该是上次已经成功收到数据字节序号加1

.
4.数据偏移,占4位,用于指出TCP首部长度,若不存在选项,则这个值为20字节,数据偏移的最大值为60字节。

5.保留字段占6位,暂时可忽略,值全为0

6.标志位

URG(紧急) : 为1时表明紧急指针字段有效
ACK(确认):为1时表明确认号字段有效
PSH(推送):为1时接收方应尽快将这个报文段交给应用层
RST(复位):为1时表明TCP连接出现故障必须重建连接
SYN(同步):在连接建立时用来同步序号
FIN (终止): 为1时表明发送端数据发送完毕要求释放连接

7.接收窗口占2个字节,用于流量控制和拥塞控制,表示当前接收缓冲区的大小。在计算机网络中,通常是用接收方的接收能力的大小来控制发送方的数据发送量。TCP连接的一端根据缓冲区大小确定自己的接收窗口值,告诉对方,使对方可以确定发送数据的字节数。

8.校验和占2个字节,范围包括首部和数据两部分。

9.选项是可选的,默认情况是不选。

三次握手与四次挥手

1、三次握手

置位概念:根据TCP的包头字段,存在3个重要的标识ACK、SYN、FIN

ACK:表示验证字段
SYN:位数置1,表示建立TCP连接
FIN:位数置1,表示断开TCP连接

这里写图片描述

第一次握手
客户机发送连接请求报文段到服务器,并进入SYN_SENT状态,等待服务器确认。(SYN = 1,seq=x)。

第二次握手
服务器收到连接请求报文,如果同意建立连接,向客户机发回确认报文段,并为该TCP连接分配TCP缓存和变量。(SYN=1,ACK=1,seq=y,ack=x+1)。

第三次握手
客户机收到服务器的确认报文段后,向服务器给出确认报文段,并且也要给该连接分配缓存和变量。此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。(ACK=1,seq=x+1,ack=y+1)。

TCP三次握手为什么不能是两次?

主要是防止两次握手情况下已经失效的连接请求报文段突然又传送到服务端而产生错误。例如,客户机A向服务器B发送TCP连接请求,第一个连接请求报文在网络的某个节点长时间滞留,A超时后认为报文丢失,于是再重传一次连接请求,B收到后建立连接。数据传输完毕后双方断开连接,而这时之前滞留的连接请求到达了服务端B,而B认为A又发来连接请求。如果两次握手建立连接,A并无连接请求,造成B的资源浪费。

2、四次挥手

这里写图片描述

1、TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送。
2、服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
3、服务器关闭客户端的连接,发送一个FIN给客户端。
4、客户端发回ACK报文确认,并将确认序号设置为收到序号加1。

为什么连接的时候是三次握手,关闭的时候却是四次握手?

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

Socket

简单的例子

server端

 public static void main(String[] args) throws IOException {
        System.out.println("等待客户端连接。。。");
        //创建一个ServerSocket
        ServerSocket ss=new ServerSocket(888);
        ss.setSoTimeout(100000000);
        Socket server = ss.accept();
        System.out.println("远程主机地址:" + server.getRemoteSocketAddress());
        //接受来自client端发来的数据
        DataInputStream in = new DataInputStream(server.getInputStream());
        //打印client发来的数据
        System.out.println(in.readUTF());
        //发给client的数据
        DataOutputStream out = new DataOutputStream(server.getOutputStream());
        out.writeUTF("谢谢连接我:" + server.getLocalSocketAddress() + "
Goodbye!");
        server.close();
    }

client端

public static void main(String[] args) throws IOException {
        Socket client = new Socket("localhost", 888);
        System.out.println("远程主机地址:" + client.getRemoteSocketAddress());
        //发数据给server端
        OutputStream outToServer = client.getOutputStream();
        DataOutputStream out = new DataOutputStream(outToServer);
        out.writeUTF("Hello from " + client.getLocalSocketAddress());
        //接受来自server端的数据
        InputStream inFromServer = client.getInputStream();
        DataInputStream in = new DataInputStream(inFromServer);
        //打印server端发来的数据
        System.out.println("服务器响应: " + in.readUTF());
        client.close();

    }

更好的例子:
参考文章:
https://kb.cnblogs.com/page/188928/
https://blog.csdn.net/sssnmnmjmf/article/details/68486261
https://www.cnblogs.com/midiyu/p/7875574.html

希望在知识中书写人生的代码
原文地址:https://www.cnblogs.com/tongxupeng/p/10259531.html