Advertisement

TCP和UDP可以使用同一个端口号吗?

阅读量:

TCP和UDP可以使用同一个端口号吗?

首先说答案:可以。怎么理解呢?

为了解决这一问题,我们可以从计算机网络通信入手.对于学习过计算机网络的学生来说,在课本中通常会提到7个或4个主要的层次结构.TCP/UDP属于其中的传输层面,具体来说是在OSI模型中位于传输层面的是TCP/UDP协议家族.而在其下一层就是所谓的"网际网",也就是所谓的"网路层级"(Network Layer),该层次的主要任务就是利用IP地址来建立节点之间的连接并实现数据包传递.这也是我们在程序开发中最常接触到的基础部分之一.至于更低层次的数据链路层级(Data Link Layer)以及物理层级(Physical Layer),则与我们的日常编程工作关系不大.

IP

让我们深入了解网络层的工作机制。在IP网络层中,默认情况下当发送方试图将数据传输给接收方时,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下 getDefault() 的作用是什么?一旦发送方获得目标机的IP地址后 getDefault() 的作用是什么?一旦发送方获得目标机的IP地址后 getDefault() 的作用是什么?一旦发送方获得目标机的IP地址后 getDefault() 的作用是什么?一旦发送方获得目标机的IP地址后 getDefault() 的作用是什么?一旦发送方获得目标机的IP地址后 getDefault() 的作用是什么?一旦发送方获得目标机的IP地址后 getDefault() 的作用是什么?一旦发送方获得目标机的 IP 地址之后, 网络设备会自动识别并分配这些 incoming packets 到相应的 application processes 上, 无需人工干预, 这种机制大大简化了复杂的通信过程, 是现代互联网得以高效运转的重要保障之一

端口号

投递者访问了一座楼群。之后如何将包裹送达相应的居民?你很快就会意识到。

在计算机领域中,端口号类似于一种独特的标识符(类似于门牌号码)。操作系统会将不同的应用程序与各自的专用端口号绑定在一起(类似于分配专有门牌号码),这样当应用程序发送数据时(类似于发出指令),不仅需要指定目标地址(如同传统的网络通信中的IP地址),还需要明确指定目标地址对应的专用端口号(相当于门牌号码),以便接收方的计算机能够将数据正确地转发到相应的程序中(类似传统的网络通信机制)。

TCP/UDP

那么是否可以在TCP和UDP之间共享同一个端口号呢?在确定端口时,操作系统会先识别数据包使用的传输层协议是TCP还是UDP,然后根据所使用的协议进行相应的解析处理.之后会将获取的数据转发至对应的程序.

所以TCP和UDP都允许使用相同的端口号进行通信,在实际应用中这种设计是广泛采用的方案之一。例如DNS(域名系统)为了实现高效的数据检索通常会同时支持TCP和UDP协议的查询机制,并通过53号端口作为统一的标准接口来进行数据接收与响应处理

但是,在同一网络通信协议下(即同一个传输层协议),每个进程必须拥有独特的端口号标识。如果存在相同的端口号配置,则会导致操作系统中的进程调度机制无法正确识别并分配相应的数据包转发任务给具体的进程或应用程序),从而引发系统性能下降、服务中断甚至数据丢失等问题

一些同学可能会注意到这样一个现象:即同一台计算机上的多个网站都使用了80或443端口。这种现象源于应用层的能力特性——所有这些网站都运行在同一Web服务器程序中。该Web服务器绑定的是80端口(以及可能的443端口),当客户端接收数据时(通常来自浏览器),Web服务器会根据HTTP协议中的主机头(本质上就是域名)将数据转发给相应的网站程序。

另外,在多台设备上配置了多个IP地址时,则反而更容易解决问题。每个IP地址对应一个独立的网络接口,在采用相同的传输控制协议(TCP)的情况下(即使都使用相同协议),只要IP地址不同设置两个端口号也不会出现冲突

"IP"加上"传输层协议"以及"端口号"组合起来就是我们常说的连接器(通道),它能够保证数据能够顺畅地在两个网络程序之间传递。普通用户如果直接使用TCP协议和UDP协议编程的话,则需要自己配置这些参数来完成相关操作。

示例

口说无凭,再给大家写个demo,使用go语言,简单易懂:

下边的程序将启动一个TCP服务器以及一个UDP服务器,并使它们绑定同一的IP地址及端口号。为了简化测试过程,程序采用了127.0.0.1这个本地机器的IP地址;当然你可以更换为局域网或公共网络上的地址。

复制代码
 package main

    
  
    
 import (
    
     "fmt"
    
     "net"
    
     "os"
    
 )
    
  
    
 func main() {
    
     // 定义监听的端口
    
     port := "127.0.0.1:12345"
    
  
    
     // 启动TCP服务器
    
     go startTCPServer(port)
    
  
    
     // 启动UDP服务器
    
     startUDPServer(port)
    
 }
    
  
    
 func startTCPServer(port string) {
    
     // 通过TCP协议监听端口
    
     l, err := net.Listen("tcp", port)
    
     if err != nil {
    
     fmt.Println("Error listening:", err.Error())
    
     os.Exit(1)
    
     }
    
     defer l.Close()
    
     fmt.Println("TCP Server Listening on " + port)
    
     
    
     // 持续接收TCP数据
    
     for {
    
     conn, err := l.Accept()
    
     if err != nil {
    
         fmt.Println("Error accepting: ", err.Error())
    
         os.Exit(1)
    
     }
    
     fmt.Println("Received TCP connection")
    
     conn.Close()
    
     }
    
 }
    
  
    
 func startUDPServer(port string) {
    
     // 通过UDP协议监听端口
    
     addr, err := net.ResolveUDPAddr("udp", port)
    
     if err != nil {
    
     fmt.Println("Error resolving: ", err.Error())
    
     os.Exit(1)
    
     }
    
  
    
     conn, err := net.ListenUDP("udp", addr)
    
     if err != nil {
    
     fmt.Println("Error listening: ", err.Error())
    
     os.Exit(1)
    
     }
    
     defer conn.Close()
    
     fmt.Println("UDP Server Listening on " + port)
    
  
    
     buffer := make([]byte, 1024)
    
  
    
     // 持续接收UDP数据
    
     for {
    
     n, _, err := conn.ReadFromUDP(buffer)
    
     if err != nil {
    
         fmt.Println("Error reading: ", err.Error())
    
         continue
    
     }
    
     fmt.Printf("Received UDP packet: %s\n", string(buffer[:n]))
    
     }
    
 }
    
    
    
    
    AI生成项目
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-07-13/wanO5Rc7poJWLhAdFyxf4ZtsVPBg.png)

然后再创建两个客户端,一个是TCP客户端:

复制代码
 package main

    
  
    
 import (
    
 	"fmt"
    
 	"net"
    
 	"os"
    
 )
    
  
    
 func main() {
    
 	// 连接到服务器
    
 	conn, err := net.Dial("tcp", "localhost:12345")
    
 	if err != nil {
    
 		fmt.Println("Error connecting:", err.Error())
    
 		os.Exit(1)
    
 	}
    
 	defer conn.Close()
    
  
    
 	// 发送数据
    
 	_, err = conn.Write([]byte("Hello TCP Server!"))
    
 	if err != nil {
    
 		fmt.Println("Error sending data:", err.Error())
    
 		return
    
 	}
    
 	fmt.Println("Message sent to TCP server")
    
 }
    
    
    
    
    AI生成项目
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-07-13/fmAJqVBKoUdNcSHehp5rWL67sPF1.png)

另一个是UDP客户端:

复制代码
 package main

    
  
    
 import (
    
 	"fmt"
    
 	"net"
    
 	"os"
    
 )
    
  
    
 func main() {
    
 	ServerAddr, err := net.ResolveUDPAddr("udp", "localhost:12345")
    
 	if err != nil {
    
 		fmt.Println("Error resolving: ", err.Error())
    
 		os.Exit(1)
    
 	}
    
  
    
 	conn, err := net.DialUDP("udp", nil, ServerAddr)
    
 	if err != nil {
    
 		fmt.Println("Error dialing: ", err.Error())
    
 		os.Exit(1)
    
 	}
    
 	defer conn.Close()
    
  
    
 	// 发送数据
    
 	_, err = conn.Write([]byte("Hello UDP Server!"))
    
 	if err != nil {
    
 		fmt.Println("Error sending data:", err.Error())
    
 		return
    
 	}
    
 	fmt.Println("Message sent to UDP server")
    
 }
    
    
    
    
    AI生成项目
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-07-13/qxtNnhaFW0lOSV9ZfQdyrRIUYv4g.png)

我们可以观察到当客户端发起请求时都使用了 localhost:12345 这个目标地址 其中 localhost 其实是一个域名 它会被本地计算机解析成 127.0.0.1 这部分如果有不明白的地方 可以参考我之前写的那篇

实际效果如下:


综上所述,在网络通信领域中存在这样一种情况:在同一台计算机上实现TCP与UDP协议使用相同端口的技术方案。每个网络进程都拥有独一无二的套接字地址这一事实是不容置疑的。该地址由三重组构成包括IP地址、传输层协议以及指定端口号三个要素。操作系统的任务就是通过解析数据包中的传输层协议类型(仅限于TCP或UDP)及其指定的端口号来确保所有数据能够准确传递给相应的应用程序。

全部评论 (0)

还没有任何评论哟~