浅析Socket网络通信(tcp协议,udp协议)

什么是Socket网络通信

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP协议的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原义那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。

网络模型:

应用层: http协议(无状态的)
传输层: tcp/udp协议
网络层: ip协议 硬件设备协议
链路层: 以太网协议 硬件设备协议

ip地址是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序而程序的pid是同一台机器上不同进程或者线程的标识。

网络通信的三要素
A. IP地址 (相当于省市区地址)
B. 端口号 (相当于具体门牌号)
C. 传输协议 (TCP/UDP 相当于沟通暗号)
IP地址
含义: 设备在网络当中的标识地址
分类: IPV4 和 IPV6
端口号
每一款软件都是一个独立的进程,进程当中的地址就是端口号
传输协议
含义: 网络数据传输的过程当中,彼此遵循的一种规则。(例如:TCP协议 和 UDP协议)

TCP协议:
A.建立连接
B.数据可靠
C.大数据传输
D.效率慢
UDP协议:
A.无需连接
B.数据不可靠
C.大小限制 64K
D.速度快

TCP协议建立连接的三次握手<A发送信息给B过程>

A询问B: 你活着吗?
B回复A: 我活着        ------------ 第一次
B询问A: 你活着吗?     ------------ 第二次
A回复B: 我也活着      ------------ 第三次

在这里插入图片描述

TCP协议建立连接的三次握手过程分析

TCP(Transmission Control Protocol) 传输控制协议
TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:
位码即tcp标志位,有6种标示:

SYN(synchronous建立联机/同步报文)
ACK(acknowledgement 确认/响应报文)
PSH(push传送)
FIN(finish结束)
RST(reset重置)
URG(urgent紧急)
Sequence number(顺序号码)
Acknowledge number(确认号码)
第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567(顺序号码)的数据包到服务器,主机B由SYN=1知道,A要求建立联机;
第二次握手:主机B收到请求后要确认联机信息,向A发送确认号码ack number=(主机A的seq+1),syn=1,ack=1
第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。
完成三次握手,主机A与主机B开始传送数据。

Tcp协议四次挥手,关闭连接的过程

在这里插入图片描述
图示过程如下:
某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;
一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
接收到这个FIN的源发送端TCP对它进行确认。
这样每个方向上都有一个FIN和ACK。

【注意】中断连接端可以是Client端,也可以是Server端。
假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,“告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息”。这个时候Client端就进入FIN_WAIT(等待状态),继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,“告诉Client端,好了,我这边数据发完了,准备好关闭连接了”。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,“就知道可以断开连接了”。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!

【问题】为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

InetAddress的概述和测试

InetAddress用来表示互联网协议 IP地址

获取对象的操作:
1.通过主机名获取InetAddress对象
InetAddress address = InetAddress.getByName(“DESKTOP-7NDF5AE”) 通过主机名去得到IP地址
2.通过IP地址获取InetAddress对象
InetAddress address = InetAddress.getByName(“192.168.1.106”);//ip地址是唯一的

扫描二维码关注公众号,回复: 9456458 查看本文章

常用的两个方法:
address.getHostName() 得到主机名
address.getHostAddress() 得到IP地址

使用UDP协议发送数据

A.使用UDP协议发送数据步骤:
a.创建 (创建发送端Socket的对象)
b.打包 (创建数据并且打包)
c.发送 (使用Socket发送数据方法)
d.释放

B.套接字DatagramSocket常用的方法:
a.构造方法
DatagramSocket() :无参构造.创建客户端Socket对象并随机分配端口号
DatagramSocket(int port) :带参构造 创建服务端Socket对象并指定端口号
b.打包数据方法
DatagramPacket dp = new DatagramPacket(bys,length,address,port);
c.发送数据的方法
ds.send(dp);
d.释放资源的方法
ds.close(); C.注意UDP协议
UDP协议是无需连接,只管发送.不需要管是否有接收。

示例代码:

发送数据:

public class Test {
    public static void main(String[] args) throws IOException {
        // 创建发送端(客户端)Socket对象
        DatagramSocket ds = new DatagramSocket();
        String s = "hello udp!";  //数据源
        byte[] bys = s.getBytes();//数据源字节数组
        int length = bys.length; //数据源大小
// 获取IP地址,发送目标
        InetAddress address = InetAddress.getByName("DESKTOP-7NDF5AE");
        int port = 8888;//端口号
// 打包 参数1:数据源字节数组数据,参数2:数据原大小,参数3:发送目标的Ip地址,参数4:端口号
        DatagramPacket dp = new DatagramPacket(bys, length, address, port);
        // 发送数据
        ds.send(dp);
        // 释放资源
        ds.close();
        System.out.println("ok");
    }
}

接收数据:

public class ReceiveTest {
    public static void main(String[] args) throws IOException {
        //创建接收端(服务端)Socket对象,构造方法为制定端口号,与发送端设定端口号匹配
        DatagramSocket ds = new DatagramSocket(8888);
        //接收数据
        //DatagramPacket(byte[] buf, int length) 
        byte[] bys = new byte[1024];//接收字节数组数据对象
//创建数据打包对象
        DatagramPacket dp = new DatagramPacket(bys,bys.length);

        System.out.println(1);
        ds.receive(dp);**//阻塞(等待其它数据传输完毕,阻塞是线程行为,如果不能立即执行此线程,则会阻塞等待)**
        System.out.println(2);

        //解析数据
        //InetAddress getAddress() : 获取发送端的IP对象
        InetAddress address = dp.getAddress();
        //byte[] getData()  :获取接收到的数据,也可以直接使用创建包对象时的数组
        byte[] data = dp.getData();
        //int getLength()  :获取具体收到数据的长度
        int length = dp.getLength();

        //输出数据
        System.out.println("sender ---> " + address.getHostAddress());
        //String类构造方法(参数1:数据源字节数组,参数2:起始位,参数3:结束位)
        System.out.println(new String(bys,0,length));
        //释放资源
        ds.close();
    }
}

TCP协议接收数据

A.使用TCP协议发送数据
a.创建(创建发送端Socket对象)
b.流对象(获取输出流的对象)
c.发送(发送数据)
d.释放(释放资源)

B.构造方法
Socket(InetAddress address, int port) //参数1:IP地址, 参数2:端口号

C.异常信息
Exception in thread “main” java.net.ConnectException: Connection refused: connect

Tcp协议发送数据示例:
public class Test {

    public static void main(String[] args) throws IOException {
        System.out.println("ready");
        // 创建发送端Socket对象(创建连接)//参数1:IP地址, 参数2:端口号
        Socket s = new Socket(InetAddress.getByName("DESKTOP-7NDF5AE"), 10086);
        // 获取输出流对象(字节流)
        OutputStream os = s.getOutputStream();
        // 发送数据
        String str = "hello tcp!!!";
        os.write(str.getBytes());//字节输出流发送字节数组数据
        // 释放资源
        // os.close();
        s.close();
    }
}

TCP协议接收数据

A.使用TCP协议接收数据的操作
a. 创建(创建接收端监听数据Scoket的对象)
b. 监听(阻塞.等待着数据的到来)
c. 获取(获取输入流对象,得到数据,输出数据)
d. 释放(释放资源)

B.常用的方法
a.构造方法 ServerSocket(int port)
b.成员方法
Socket accept() 获取到客户端的Socket对象
InputStream getInputStream() 获取到输入流的对象

C.注意运行的顺序
a.先运行服务端
b.再运行客户端
c.查看服务端的控制台信息

Tcp协议接收数据示例:
public class ServerTest {
    public static void main(String[] args) throws IOException  {
        //创建服务端接收端Socket对象,参数为发送数据指定的端口号
        ServerSocket ss = new ServerSocket(10086);
        //监听(阻塞,等待其它数据传输完毕,阻塞是线程行为,如果不能立即执行此线程,则会阻塞等待)
        Socket s = ss.accept();//获取到客户端Socket
        //获取字节输入流对象(读取数据)
        InputStream is = s.getInputStream();
        //获取数据
        byte[] bys = new byte[1024];
        int len;//用于存储读到的字节个数
        len = is.read(bys);
        //输出数据
        InetAddress address = s.getInetAddress();
        System.out.println("client ---> " + address.getHostName());
        System.out.println(new String(bys,0,len));
        //释放资源
        s.close();
        //ss.close();
    }
}
发布了32 篇原创文章 · 获赞 53 · 访问量 2489

猜你喜欢

转载自blog.csdn.net/qq_41714882/article/details/103818071