您的位置 首页 > AI资讯 > 产业资讯

如何区分同一端口上的不同套接字

套接字就是套接字。通过将这三个参数组合起来,绑定到一个socket上,应用层就可以通过socket接口与传输层进行通信,以区分来自不同应用程序或网络连接点的通信,从而实现数据传输。并发服务。

Accept()生成的Socket端口号是多少?

编写网络程序,必须使用Sockets。这是程序员都知道的。面试的时候我们还会问对方是否懂socket编程。很多人会说Socket编程基本上就是由监听、接受、发送和写入组成。几个基本操作与普通文件操作相同。只要你写过它们,你就会认识它们。

对于网络编程,我们必须称之为TCP。好像其他的网络协议已经不存在了。对于TCP,我们还知道UDP。前者可以保证数据的准确性和可靠性,后者允许数据丢失。最后我们还知道,在建立连接之前,必须知道对方的IP地址和端口号。另外,普通程序员不会知道太多。在许多情况下,这些知识就足够了。最多在编写服务程序时使用多线程。并发访问。

我们还知道以下事实

1. 指定的端口号不能被多个应用程序共享。例如,如果IIS占用80端口,那么APACHE就不能使用该端口。

2. 许多防火墙只允许来自特定目的端口的数据包通过。

3、服务程序监听某个端口并接受连接请求后,会生成一个新的socket来处理该请求。

那么一个困扰我很久的问题就出现了。如果创建了一个socket并绑定了80端口,那么是否意味着该socket占用了80端口?

如果是这种情况,那么当它接受请求时,新套接字使用哪个端口? (我一直以为系统会默认给它分配一个空闲端口号)

如果是空闲端口号,那么一定不是80端口,所以以后的TCP数据包的端口肯定不是80——防火墙会阻止它通过。

事实上,我们可以看到防火墙并没有阻止这样的连接,而这也是最常见的连接请求和处理方式。我不明白的是为什么防火墙不阻止这样的连接。它如何判断连接是因为connect80端口生成的呢? TCP数据包中是否有特殊标记,或者防火墙是否记住了什么?

后来我仔细阅读了TCP/IP协议栈的原理,对很多概念有了更深入的理解。例如,TCP和UDP都属于传输层,共同假设在IP层之上,而IP层主要负责节点之间的通信。对于数据包的传输来说,这里的节点是网络设备,比如计算机。由于IP只负责向节点发送数据,无法区分上述不同的应用,所以TCP和UDP在协议中增加了端口。信息,端口标识节点上的应用程序,并处理添加端口信息。 UDP协议基本上不处理IP层的数据,TCP协议还增加了更复杂的传输控制,例如滑动数据传输。窗口,以及接受确认和重发机制,实现数据的可靠传输。无论应用层看到什么样的稳定的TCP数据流,下面传输的都是一个接一个的IP数据包,需要TCP协议进行处理。执行数据重组。

TCP/IP只是一个协议栈。就像操作系统的运行机制一样,必须具体实现。同时必须提供外部操作接口。就像操作系统提供标准的编程接口,如Win32编程接口一样,TCP、IP也必须向外界提供编程接口。这就是Socket 编程接口- 就是这样。

如何区分同一端口上的不同套接字

在Socket编程接口中,设计者提出了一个非常重要的概念,那就是socket。这个套接字与文件句柄非常相似。事实上,在BSD系统中,它与文件句柄存储在同一个进程句柄中。这个socket实际上是一个序列号,表明它在句柄表中的位置。我们见过很多这样的情况,比如文件句柄、窗口句柄等,这些句柄实际上代表了系统中某些特定的对象,在各种函数中作为参数来操作特定的对象。 —— 这其实是C语言的问题。在C++语言中,this句柄其实就是this指针,它实际上是一个对象指针。

现在我们知道socket与TCP/IP没有必然的关系。设计Socket编程接口时,希望它也能适配其他网络协议。所以socket的出现只是让TCP/IP协议栈的使用变得更加简单。它对TCP/IP进行了抽象,形成了几个基本的功能接口。例如创建、监听、接受、连接、读写等。

需要明白的是,socket只是TCP/IP协议栈操作的抽象,并不是简单的映射关系!

昨天和朋友聊了网络编程。关于Socket,我在这里写下我个人的一些理解:)

程序中可以创建Socket,分为普通Socket和原始Socket两种。

1:普通Socket是TCP/IP协议栈中传输层操作的编程接口(一种API)。

有面向连接的流式套接字(SOCK_STREAM),它是TCP的一个应用;

没有连接数据包的socket(SOCK_DGRAM),这是UDP方式的应用。

关于普通Sockets,我曾经有过一个模糊的问题。在多线程情况下,服务器监听某个端口(假设8080)后,每次接受客户端连接都会生成一个新的Socket。那么这些新生成的Socket的端口是什么呢?程序中肯定没有指定,所以应该有两种可能,1:生成随机端口。 2:仍然是端口8080。仔细想想,第一个假设似乎是不可能的。防火墙很可能会阻止来自这些随机端口的数据包。然后还有第二个假设,服务器端口仍然是8080。但这颠覆了我原来的理解,即“如果一个端口被某个程序占用,其他程序就无法使用该端口”。我认为最有可能的原因是范围不同:程序之间不能使用相同的端口,但程序内不同的Socket仍然可以使用相同的端口。因此,为了使“客户端在同一端口(8080)上向服务器发送的来自不同线程(即不同Socket连接)的数据包能够被区分和组合”,必须有一个区分特征来区分来自不同线程的数据包。连接。即传输层头中的源端口,即Socket连接中客户端的端口。综上所述,这种情况下,传输层头中的源端口(客户端)将与生成的Socket不同,但接收端口将相同(服务器)。

文章中回答了很多问题,特别是“端口是用来区分不同的应用程序”。之前我也很困惑,多线程下每个线程建立连接的端口是否可以相同。另外,文章中说,每次接受连接时,都会获得一个新的套接字。这个表达并不完美。事实上,它不是一个新的套接字,而是客户端的套接字。这个socket中的IP和端口当然是客户端的IP和端口。表示accept客户端后,服务器使用哪个端口。

单个进程监听多个端口

单个进程创建多个套接字并将它们绑定到不同的端口(TCP 或UDP)。

多个进程监听同一端口

这可以通过fork创建子进程来实现,但其他情况则不行。

如何区分同一端口上的不同套接字

在多线程情况下,服务器监听某个端口后,每次接受客户端连接都会生成一个新的Socket。

新生成的Socket的端口是多少?

答案是服务器端口或监听端口。

进程之间不能使用相同的端口,但进程内的不同Socket可以使用相同的端口。

如何区分同一端口上Client向Server发送的不同Socket。

使用Client Socket端口来区分!

Socket是TCP/IP协议的网络接口。 Socket是TCP/IP协议操作的抽象。

客户端连接函数启动调用该函数的过程并准确返回三次握手。如果第三次握手成功则返回

服务器端三次握手后,内核调用accept函数。执行accept函数后,会生成一个新的socket来连接客户端。

Q: 编写TCP/SOCK_STREAM服务程序时,SO_REUSEADDR是什么意思?

A: 此套接字选项通知内核,如果端口繁忙但TCP 状态处于TIME_WAIT,则可以重用该端口。如果端口繁忙,

TCP状态处于其他状态,并且当重新使用该端口时,我仍然收到一条错误消息,指示“该地址已在使用中”。如果您的服务程序停止

如果你想立即重新启动,但新的套接字仍然使用相同的端口,SO_REUSEADDR选项非常有用。必须认识到,此时任何

意外数据的到来可能会导致服务程序响应混乱,但这只是一种可能性,实际上可能性很小。

如何区分同一端口上的不同套接字

端口复用最常见的用途是防止在服务器重启或者程序突然退出而系统没有释放端口时,之前绑定的端口被释放。这种情况下,如果设置了端口复用,则新启动的服务器进程可以直接绑定端口。如果不设置端口复用,绑定会失败,提示ADDR已被使用。 —— 那你就得等一下再试了,麻烦!

那么如何让sockfd_one和sockfd_two套接字都成功绑定到8000端口呢?这时候就需要复用端口了。端口重用允许应用程序将n 个套接字绑定到一个端口而不会出现错误。

设置socket的SO_REUSEADDR选项来实现端口复用:

int opt=1;//sockfd是需要端口复用的socket setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)opt, sizeof(opt)); SO_REUSEADDR可以在以下四种情况下使用。 (摘自《Unix网络编程》 第1 卷,UNPv1)

1、当有一个本地地址和端口相同的socket1处于TIME_WAIT状态,而你启动的程序的socket2需要占用该地址和端口时,你的程序就会使用这个选项。

2. SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例(多个进程)。但每个实例绑定的IP地址不能相同。这种情况可以在多网卡或者IP Alias技术的机器上进行测试。

3、SO_REUSEADDR允许单个进程将同一个端口绑定到多个socket,但每个socket绑定的IP地址不同。这与2 非常相似,请参阅UNPv1 了解差异。

4. SO_REUSEADDR 允许重复绑定完全相同的地址和端口。但这仅适用于UDP 多播,不适用于TCP。

需要注意的是,绑定前必须调用端口复用函数,绑定到同一端口的所有套接字都必须被复用:

//sockfd_one和sockfd_two都必须设置端口复用//sockfd_one绑定bind之前,设置其端口复用int opt=1;setsockopt( sockfd_one, SOL_SOCKET,SO_REUSEADDR, (const void *)opt, sizeof(opt) ) ;err_log=绑定(sockfd_one, (struct sockaddr*)my_addr, sizeof(my_addr)); //sockfd_two绑定bind之前,设置其端口复用opt=1;setsockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR,(const void * )opt, sizeof(opt) );err_log=bind(sockfd_two, (struct sockaddr*)my_addr, sizeof (我的地址));端口多路复用允许应用程序将n 个套接字绑定到一个端口,而不会出现问题。同时这n个socket正常发送信息,没有问题。但是,并不是所有这些套接字都能读取信息,只有最后一个套接字会正常接收数据。

#include stdio.h#include stdlib.h#include string.h#include unistd.h#include sys/socket.h#include netinet/in.h#include arpa/inet.h#include pthread.h //线程1回调函数void *recv_one(void *arg){printf('============recv_one==============\n');int sockfd=( int )arg;while(1){int recv_len;char recv_buf[512]='';struct sockaddr_in client_addr;char cli_ip[INET_ADDRSTRLEN]='';//INET_ADDRSTRLEN=16socklen_t cliaddr_len=sizeof(client_addr);recv_len=recvfrom ( sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)client_addr, cliaddr_len);inet_ntop(AF_INET, client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);printf('\nip:%s ,port:%d\n', cli_ip , ntohs(client_addr.sin_port));printf('sockfd_one===========data(%d):%s\n',recv_len,recv_buf);} return NULL;} //线程2 回调函数void *recv_two(void *arg){printf('++++++++++recv_two++++++++++++++++\n');int sockfd=(int )arg;while(1){int recv_len;char recv_buf [512 ]='';struct sockaddr_in client_addr;char cli_ip[INET_ADDRSTRLEN]='';//INET_ADDRSTRLEN=16socklen_t cliaddr_len=sizeof(client_addr);recv_len=recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *) client_addr, cliaddr_len);inet_ntop(AF_INET, client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);printf('\nip:%s ,port:%d\n',cli_ip, ntohs(client_addr.sin_port));printf('sockfd_two @ @@ @@@@@@@@@@@ data(%d):%s\n',recv_len,recv_buf);} 返回NULL;} int main(int argc, char *argv[]){int err_log ; ///////////////////////////sockfd_oneint sockfd_one;sockfd_one=套接字(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字oneif(sockfd_one 0 ){perror('sockfd_one');exit(-1);} //设置本地网络信息struct sockaddr_in my_addr;bzero(my_addr, sizeof(my_addr));my_addr.sin_family=AF_INET ;my_addr.sin_port=htons(8000) ;//端口为8000my_addr.sin_addr.s_addr=htonl(INADDR_ANY);//sockfd_one绑定bind之前,设置其端口复用int opt=1;setsockopt( sockfd_one, SOL_SOCKET, SO_REUSEADDR, (const void *)opt, sizeof(opt) ); //绑定,端口为8000err_log=bind(sockfd_one, (struct sockaddr*)my_addr, sizeof(my_addr));if(err_log !=0){perror('bind sockfd_one');close( sockfd_one);exit(-1 );}//接收信息线程1pthread_t tid_one;pthread_create(tid_one, NULL, recv_one, (void *)sockfd_one);///////////////////////////sockfd_twoint sockfd_two;sockfd_two=套接字(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字twoif(sockfd_two 0){perror('sockfd_two');exit(-1);} //在sockfd_two绑定bind之前,设置其端口复用opt=1; setockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR, (const void *)opt, sizeof(opt) ); //新建socket sockfd_two,继续绑定8000端口,成功err_log=bind(sockfd_two, (struct sockaddr*)my_addr, sizeof(my_addr));if(err_log !=0){perror('bind sockfd_two');close( sockfd_two);exit(-1);} //接收信息线程2pthread_t tid_two;pthread_create(tid_two, NULL, recv_two, (void *)sockfd_two);while(1){//让程序阻塞在这里,不结束NULL;}关闭(sockfd_one);关闭(sockfd_two); return 0;} 然后通过网络调试助手向该服务器发送数据。结果显示只有最后一个socket sockfd_two会正常接收数据。

一个连接的唯一标识是[服务器ip,服务器端口,客户端ip,客户端端口],也就是说。当操作系统从某个端口接收数据时,它会找到与这个唯一标识符匹配的端口生成的连接,并将信息传输到相应的缓冲区。

用户评论

♂你那刺眼的温柔

同一个端口可以有多个socket,关键在于区分它们的连接地址和协议类型

    有10位网友表示赞同!

别留遗憾

可以用套接字的文件描述符来区分不同socket。

    有14位网友表示赞同!

笑傲苍穹

可以通过分析协议头信息来判断 socket 的类型!

    有6位网友表示赞同!

命运不堪浮华

TCP、UDP 协议使用的端口号相同

    有5位网友表示赞同!

绳情

学习一下 socket API 的使用,可以轻松解决这个问题

    有20位网友表示赞同!

志平

这篇文章太少了,不够详细啊

    有13位网友表示赞同!

太难

看题就知道是需要区分不同协议的socket!

    有19位网友表示赞同!

雁過藍天

同一个端口下同时存在的多个应用程序。

    有5位网友表示赞同!

凉月流沐@

比如FTP和HTTP都是使用这个端口

    有11位网友表示赞同!

花开丶若相惜

学习一下 Linux 的网络编程知识

    有18位网友表示赞同!

纯情小火鸡

端口号只是标识一种服务的类别

    有15位网友表示赞同!

寂莫

Socket 文件描述符是区分唯一的标志!

    有14位网友表示赞同!

妄灸

同一个端口,同一个协议,用不同的应用程序运行就会区分开来

    有7位网友表示赞同!

蝶恋花╮

学习linux网络编程基础必不可少

    有13位网友表示赞同!

琴断朱弦

端口号不是唯一标识的,需要结合其他信息进行区分。很实用。

    有17位网友表示赞同!

陌颜

太棒了!

    有8位网友表示赞同!

本站涵盖的内容、图片、视频等数据,部分未能与原作者取得联系。若涉及版权问题,请及时通知我们并提供相关证明材料,我们将及时予以删除!谢谢大家的理解与支持!

Copyright © 2023