网络编程(一)TCP编程和UDP编程

目录

一、网络编程基础

1.概述

[2. IP地址](#2. IP地址)

[3. 域名](#3. 域名)

[4. 网络模型](#4. 网络模型)

[5. 常用协议](#5. 常用协议)

[6. 小结](#6. 小结)

二、TCP编程

1.什么是Socket?

[2. 服务器端](#2. 服务器端)

[3. 客户端](#3. 客户端)

[4. Socket流](#4. Socket流)

[5. 小结](#5. 小结)

三、UDP协议

1.概述

[2. 服务器端](#2. 服务器端)

[3. 客户端](#3. 客户端)

[4. 小结](#4. 小结)


一、网络编程基础

1.概述

计算机网络是指两台或更多的计算机组成的网络,在同一个网络中,任意两台计算机都可以直接通信,因为所有计算机都需要遵循同一种网络协议。

那什么是互联网呢?互联网是网络的网络(internet),即把很多计算机网络连接起来,形成一个全球统一的互联网。对某个特定的计算机网络来说,它可能使用网络协议ABC,而另一个计算机网络可能使用网络协议XYZ。如果计算机网络各自的通讯协议不统一,就没法把不同的网络连接起来形成互联网。因此,为了把计算机网络接入互联网,就必须使用TCP/IP协议。

TCP/IP协议泛指互联网协议,其中最重要的两个协议是TCP协议IP协议。只有使用TCP/IP协议的计算机才能够联入互联网,使用其他网络协议(例如NetBIOSAppleTalk协议等)是无法联入互联网的。

中国四大主流网络体系Chinanet、``CERNET、``cstnet、``CHINAGBN

1.Chinanet是邮电部门经营管理的基于Internet网络技术的中国公用计算机互联网,是国际计算机互联网(Internet)的一部分,是中国的Internet骨干网。

2.CERNET中国教育和科研计算机网CERNET是由国家投资建设,教育部负责管理,清华大学等高等学校承担建设和管理运行的全国性学术计算机互联网络。

3.cstnet 1994年中国科学技术网CSTNET首次实现和Internet直接连接,同时建立了中国最高域名服务器,标志着中国正式接入Internet

4.ChinaGBN(China Golden Bridge Network)也称做中国国家公用经济信息通信网。它是中国国民经济信息化的基础设施,是建立金桥工程的业务网,支持金关、金税、金卡等"金"字头工程的应用。

2. IP地址

在互联网中,一个IP地址用于唯一标识一个网络接口(Network Interface)。一台联入互联网的计算机肯定有一个IP地址,但也可能有多个IP地址(多个网卡)。

IP地址分为IPv4IPv6两种。IPv4采用32位地址,类似101.202.99.12,而IPv6采用128位地址,类似2001:0DA8:100A:0000:0000:1020:F2F3:1428IPv4地址总共有2 32个(大约42亿),而IPv6地址则总共有2 128个(大约340万亿亿亿亿),IPv4的地址目前已耗尽,而IPv6的地址是根本用不完的。

IP地址又分为公网IP地址内网IP地址。公网IP地址可以直接被访问,内网IP地址只能在内网访问。内网IP地址类似于:

192.168.x.x

10.x.x.x

有一个特殊的IP地址,称之为本机地址,它总是127.0.0.1

IPv4地址实际上是一个32位整数。例如

java 复制代码
1707762444 = 0x65ca630c
           = 65  ca  63 0c
           = 101.202.99.12

如果一台计算机只有一个网卡,并且接入了网络,那么,它有一个本机地址127.0.0.1,还有一个IP地址,例如101.202.99.12,可以通过这个IP地址接入网络。如果一台计算机有两块网卡,那么除了本机地址,它可以有两个IP地址,可以分别接入两个网络。通常连接两个网络的设备是路由器或者交换机,它至少有两个IP地址,分别接入不同的网络,让网络之间连接起来。

如果两台计算机位于同一个网络,那么他们之间可以直接通信,因为他们的IP地址前段部分是相同的,也就是网络号是相同的。网络号是IP地址通过子网掩码过滤后得到的。例如:某台计算机的IP101.202.99.2,子网掩码是255.255.255.0,那么计算该计算机的网络号是:

java 复制代码
IP = 101.202.99.2
Mask = 255.255.255.0
Network = IP & Mask = 101.202.99.0

每台计算机都需要正确配置IP地址子网掩码,根据这两个就可以计算网络号,如果两台计算机计算出的网络号相同,说明两台计算机在同一个网络,可以直接通信。如果两台计算机计算出的网络号不同,那么两台计算机不在同一个网络,不能直接通信,它们之间必须通过路由器或者交换机这样的网络设备间接通信,我们把这种设备称为网关

网关的作用就是连接多个网络,负责把来自一个网络的数据包发到另一个网络,这个过程叫路由。所以,一台计算机的一个网卡会有3个关键配置:

1.IP地址,例如:10.0.2.15

2.子网掩码,例如:255.255.255.0

3.网关的IP地址,例如:10.0.2.2

有一个特殊的本机域名localhost,它对应的IP地址总是本机地址127.0.0.1

Windows操作系统中,可以通过ipconfig命令查看本地主机的IP地址:

Java中,可以通过InetAddress类的静态方法getLocalHost()来获取本地主机的IP地址:

java 复制代码
InetAddress localIPAddress = InetAddress.getLocalHost();
System.out.println("本地主机IP:" + localIPAddress.getHostAddress());
System.out.println("本地主机名称:" + localIPAddress.getHostName());

如果想检查当前主机与目标主机之间的网络是否通畅,可以使用ping命令来进行测试:

Java中,如果需要测试网络是否通畅,可以使用Runtime对象exec()执行ping命令:

java 复制代码
Process process = Runtime.getRuntime().exec("ping 192.168.254.162");

BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
while((line = reader.readLine()) != null) {
    System.out.println(line);
}

3. 域名

因为直接记忆IP地址非常困难,所以我们通常使用域名访问某个特定的服务。域名解析服务器DNS负责把域名翻译成对应的IP,客户端再根据IP地址访问服务器。

nslookup可以查看域名对应的IP地址:

4. 网络模型

由于计算机网络从底层的传输到高层的软件设计十分复杂,要合理地设计计算机网络模型,必须采用分层模型,每一层负责处理自己的操作。OSIOpen System Interconnect )网络模型是ISO组织定义的一个计算机互联的标准模型,注意它只是一个定义,目的是为了简化网络各层的操作,提供标准接口便于实现和维护。这个模型从上到下依次是:

  • 应用层,提供应用程序之间的通信;
  • 表示层:处理数据格式,加解密等等;
  • 会话层:负责建立和维护会话;
  • 传输层:负责提供端到端的可靠传输;
  • 网络层:负责根据目标地址选择路由来传输数据;
  • 数据链路层和物理层:负责把数据进行分片并且真正通过物理网络传输,例如,无线网、光纤等。

互联网实际使用的TCP/IP模型并不是对应到OSI7层模型,而是大致对应OSI5层模型

5. 常用协议

IP协议是一个分组交换协议 ,它不保证可靠传输。而TCP协议传输控制协议 ,它是面向连接的协议,支持可靠传输双向通信TCP协议是建立在IP协议之上的,简单地说,IP协议只负责发数据包,不保证顺序和正确性,而TCP协议负责控制数据包传输,它在传输数据之前需要先建立连接,建立连接后才能传输数据,传输完后还需要断开连接。TCP协议之所以能保证数据的可靠传输,是通过接收确认超时重传这些机制实现的。并且,TCP协议允许双向通信,即通信双方可以同时发送和接收数据。

TCP协议也是应用最广泛的协议,许多高级协议都是建立在TCP协议之上的,例如HTTPSMTP等。

UDP协议(User Datagram Protocol)是一种数据报文协议,它是无连接协议不保证可靠传输。因为UDP协议在通信前不需要建立连接,因此它的传输效率比TCP高,而且UDP协议比TCP协议要简单得多。选择UDP协议时,传输的数据通常是能容忍丢失的,例如,一些语音视频通信的应用会选择UDP协议。

6. 小结

(1)计算机网络:由两台或更多计算机组成的网络;

(2)互联网:连接网络的网络;

(3)IP地址:计算机的网络接口(通常是网卡)在网络中的唯一标识;

(4)网关:负责连接多个网络,并在多个网络之间转发数据的计算机,通常是路由器或交换机;

(5)网络协议:互联网使用TCP/IP协议,它泛指互联网协议簇;

(6)IP协议:一种分组交换传输协议;

(7)TCP协议:一种面向连接,可靠传输的协议;

(8)UDP协议:一种无连接,不可靠传输的协议。

二、TCP编程

1.什么是Socket?

在开发网络应用程序的时候,会遇到Socket这个概念。Socket是一个抽象概念,一个应用程序通过一个Socket来建立一个远程连接,而Socket内部通过TCP/IP协议把数据传输到网络。

java 复制代码
┌───────────┐                                   ┌───────────┐
│Application│                                   │Application│
├───────────┤                                   ├───────────┤
│  Socket   │                                   │  Socket   │
├───────────┤                                   ├───────────┤
│    TCP    │                                   │    TCP    │
├───────────┤      ┌──────┐       ┌──────┐      ├───────────┤
│    IP     │<────>│Router│<─────>│Router│<────>│    IP     │
└───────────┘      └──────┘       └──────┘      └───────────┘

SocketTCP和部分IP的功能都是由操作系统提供的,不同的编程语言只是提供了对操作系统调用的简单的封装。例如:Java提供的几个Socket相关的类就封装了操作系统提供的接口:ServerSocket类、Socket类。

为什么需要Socket进行网络通信?因为仅仅通过IP地址进行通信是不够的,同一台计算机同一时间会运行多个网络应用程序,例如浏览器、QQ、邮件客户端等。当操作系统接收到一个数据包的时候,如果只有IP地址,它没法判断应该发给哪个应用程序,所以,操作系统抽象出Socket接口,每个应用程序需要各自对应到不同的Socket,数据包才能根据Socket正确地发到对应的应用程序。

一个Socket就是由IP地址和端口号(范围是0~65535)组成,可以把Socket简单理解为IP地址+端口号。端口号总是由操作系统分配,它是一个065535之间的数字,其中,小于1024的端口属于特权端口,需要管理员权限,大于1024的端口可以由任意用户的应用程序打开。

所以,如果需要与指定主机进行通信,完整的通信地址是由一个IP地址+端口号组成:

  • 101.202.99.2:1201
  • 101.202.99.2:1304
  • 101.202.99.2:15000

使用Socket进行网络编程时,本质上就是两个进程之间的网络通信。其中一个进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它必须主动连接服务器的IP地址和指定端口,如果连接成功,服务器端和客户端就成功地建立了一个TCP连接,双方后续就可以随时发送和接收数据。

因此,当Socket连接成功地在服务器端和客户端之间建立后:

对服务器端来说:它的Socket是指定的IP地址和指定的端口号;

对客户端来说:它的Socket是它所在计算机的IP地址和一个由操作系统分配的随机端口号。

2. 服务器端

要使用Socket编程,我们首先要编写服务器端程序。Java标准库提供了ServerSocket来实现对指定IP和指定端口的监听。ServerSocket的典型实现代码如下:

java 复制代码
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(6666); // 监听指定端口
        System.out.println("server is running...");
        while (true) {
            Socket sock = ss.accept();
            
            // 使用Socket流进行网络通信
            // ...
            
            System.out.println("connected from " + sock.getRemoteSocketAddress());
        }
    }
}

服务器端通过下述代码,在指定端口6666监听。这里我们没有指定IP地址,表示在计算机的所有网络接口上进行监听。

java 复制代码
ServerSocket ss = new ServerSocket(6666);

如果ServerSocket监听成功,我们就使用一个无限循环来处理客户端的连接,注意到代码ss.accept()表示每当有新的客户端连接进来后,就返回一个Socket实例,这个Socket实例就是用来和刚连接的客户端进行通信的。

java 复制代码
  while (true) {
      Socket sock = ss.accept();
      System.out.println("connected from " + sock.getRemoteSocketAddress());
  }

如果没有客户端连接进来,accept()方法会阻塞并一直等待。如果有多个客户端同时连接进来,ServerSocket会把连接扔到队列里,然后一个一个处理。对于Java程序而言,只需要通过循环不断调用accept()就可以获取新的连接。

3. 客户端

相比服务器端,客户端程序就要简单很多。一个典型的客户端程序如下:

java 复制代码
public class Client {
    public static void main(String[] args) throws IOException {
        // 连接指定服务器和端口
        Socket sock = new Socket("localhost", 6666); 
        
       // 使用Socket流进行网络通信
       // ...
        
        // 关闭
        sock.close();
        System.out.println("disconnected.");
    }
}

客户端程序通过下述代码,连接到服务器端,注意上述代码的服务器地址是"localhost",表示本机地址,端口号是6666。如果连接成功,将返回一个Socket实例,用于后续通信。

java 复制代码
Socket sock = new Socket("localhost", 6666);

4. Socket流

Socket连接创建成功后,无论是服务器端,还是客户端,我们都使用Socket实例进行网络通信。因为TCP是一种基于流的协议,因此,Java标准库使用InputStreamOutputStream来封装Socket的数据流,这样我们使用Socket的流,和普通IO流类似:

java 复制代码
// 用于读取网络数据:
InputStream in = sock.getInputStream();

// 用于写入网络数据:
OutputStream out = sock.getOutputStream();

写入网络数据时,必须要调用flush()方法。如果不调用flush(),我们很可能会发现,客户端和服务器都收不到数据,这并不是Java标准库的设计问题,而是我们以流的形式写入数据的时候,并不是一写入就立刻发送到网络,而是先写入内存缓冲区,直到缓冲区满了以后,才会一次性真正发送到网络,这样设计的目的是为了提高传输效率。如果缓冲区的数据很少,而我们又想强制把这些数据发送到网络,就必须调用flush()强制把缓冲区数据发送出去。

5. 小结

使用Java进行TCP编程时,需要使用Socket模型:

(1)服务器端用ServerSocket监听指定端口;

(2)客户端使用Socket(InetAddress, port)连接服务器;

(3)服务器端用accept()接收连接并返回Socket实例;

(4)双方通过Socket打开InputStream/OutputStream读写数据;

(5)服务器端通常使用多线程同时处理多个客户端连接,利用线程池可大幅提升效率;

​​​​​​​(6)flush()方法用于强制输出缓冲区到网络。

三、UDP协议

1.概述

TCP编程相比,UDP编程就简单得多,因为UDP没有创建连接,数据包也是一次收发一个,所以没有流的概念。

Java中使用UDP编程,仍然需要使用Socket,因为应用程序在使用UDP时必须指定网络接口(IP地址)和端口号。注意:UDP端口和TCP端口虽然都使用0~65535,但他们是两套独立的端口,即一个应用程序用TCP协议占用了端口1234,不影响另一个应用程序用UDP协议占用端口1234

2. 服务器端

在服务器端,使用UDP也需要监听指定的端口。Java提供了DatagramSocket来实现这个功能,代码如下:

java 复制代码
DatagramSocket ds = new DatagramSocket(6666); // 监听指定端口
while (true) { // 无限循环
    // 数据缓冲区:
    byte[] buffer = new byte[1024];
    DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
    ds.receive(packet); // 收取一个UDP数据包
    // 收取到的数据存储在buffer中,由packet.getOffset(), packet.getLength()指定起始位置和长度
    // 将其按UTF-8编码转换为String:
    String s = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
    // 发送数据:
    byte[] data = "ACK".getBytes(StandardCharsets.UTF_8);
    packet.setData(data);
    ds.send(packet);
}

服务器端首先使用如下语句在指定的端口监听UDP数据包:

java 复制代码
DatagramSocket ds = new DatagramSocket(6666);

如果没有其他应用程序占据这个端口,那么代表监听成功。为了能够反复处理数据,我们使用一个死循环来处理收到的UDP数据包:

java 复制代码
while (true) { // 死循环

}

要接收一个UDP数据包,需要准备一个byte[]缓冲区,并通过DatagramPacket实现接收:

java 复制代码
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet);

假设我们收取到的是一个String,那么,通过DatagramPacket返回的packet.getOffset()packet.getLength()确定数据在缓冲区的起止位置:

java 复制代码
String s = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);

当服务器收到一个DatagramPacket后,通常必须立刻回复一个或多个UDP包,因为客户端地址在DatagramPacket中,每次收到的DatagramPacket可能是不同的客户端,如果不回复,客户端就收不到任何UDP包。

发送UDP包也是通过DatagramPacket实现的:

java 复制代码
byte[] data = ...
packet.setData(data);
ds.send(packet);

3. 客户端

和服务器端相比,客户端使用UDP时,只需要直接向服务器端发送UDP包,然后接收返回的UDP包:

java 复制代码
DatagramSocket ds = new DatagramSocket();
ds.setSoTimeout(1000);
ds.connect(InetAddress.getByName("localhost"), 6666); // 连接指定服务器和端口

// 发送:
byte[] data = "Hello".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length);
ds.send(packet);

// 接收:
byte[] buffer = new byte[1024];
packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet);
String resp = new String(packet.getData(), packet.getOffset(), packet.getLength());
ds.disconnect();

客户端打开一个DatagramSocket使用以下代码:

java 复制代码
DatagramSocket ds = new DatagramSocket();
ds.setSoTimeout(1000);
ds.connect(InetAddress.getByName("localhost"), 6666);

客户端创建DatagramSocket实例时并不需要指定端口,而是由操作系统自动指定一个当前未使用的端口。紧接着,调用setSoTimeout(1000)设定超时1秒,意思是后续接收UDP包时,等待时间最多不会超过1秒,否则在没有收到UDP包时,客户端会无限等待下去。这一点和服务器端不一样,服务器端可以无限等待,因为它本来就被设计成长时间运行。

注意到客户端的DatagramSocket还调用了一个connect()方法"连接"到指定的服务器端。不是说UDP是无连接的协议吗?为啥这里需要connect()

这个connect()方法不是真连接,它是为了在客户端的DatagramSocket实例中保存服务器端的IP和端口号,确保这个DatagramSocket实例只能往指定的地址和端口发送UDP包,不能往其他地址和端口发送。这么做不是UDP的限制,而是Java内置了安全检查。

如果客户端希望向两个不同的服务器发送UDP包,那么它必须创建两个DatagramSocket实例。后续的收发数据和服务器端是一致的。通常来说,客户端必须先发UDP包,因为客户端不发UDP包,服务器端就根本不知道客户端的地址和端口号。

如果客户端认为通信结束,就可以调用disconnect()断开连接。注意:disconnect()也不是真正地断开连接,它只是清除了客户端DatagramSocket实例记录的远程服务器地址和端口号.这样,DatagramSocket实例就可以连接另一个服务器端。

4. 小结

(1)使用UDP协议通信时,服务器和客户端双方无需建立连接;

(2)服务器端用DatagramSocket(port)监听端口;

(3)客户端使用DatagramSocket.connect()指定远程地址和端口;

(4)双方通过receive()send()读写数据;

(5)DatagramSocket没有IO流接口,数据被直接写入byte[]缓冲区;

相关推荐
斯~内克9 小时前
深入解析域名并发请求限制与HTTP/2多路复用技术
网络·网络协议·http
weixin_456904279 小时前
基于UDP的SNMP协议
网络·网络协议·udp
机器视觉知识推荐、就业指导10 小时前
手动开发一个TCP客户端调试工具(一):了解Qt中TCP通信原理与核心类
网络·qt·tcp/ip
穗 禾10 小时前
github与git新手教程(快速访问github)
网络·git·github
都给我10 小时前
零信任网络概念及在网络安全中的应用
网络·安全·web安全
卓豪终端管理11 小时前
电脑远程关机的重要性
运维·网络·devops
van叶~11 小时前
Linux网络-------3.应⽤层协议HTTP
linux·网络·http
Wmenghu11 小时前
java获取电脑公网IP和内网IP
服务器·网络·tcp/ip
小猪咪piggy12 小时前
【JavaEE】(7) 网络原理 TCP/IP 协议
运维·服务器·网络
是瑶瑶子啦14 小时前
【AlphaFold3】网络架构篇(5)|Template embedding & Pairformer stack
网络·embedding