大家好,关于如何区分同一端口上的不同套接字很多朋友都还不太明白,今天小编就来为大家分享关于的知识,希望对各位有所帮助!
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允许单个进程将同一个端口绑定到多个套接字,但每个套接字绑定的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正常发送信息,没有问题。但是,并不是所有这些socket都能读取信息,只有最后一个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,客户端端口],也就是说。当操作系统从某个端口接收数据时,它会找到与这个唯一标识符匹配的端口生成的连接,并将信息传输到相应的缓冲区。
原创文章,作者:小su,如若转载,请注明出处:https://www.sudun.com/ask/134748.html
用户评论
万象皆为过客
这个文章讲的确实很有用!我之前也遇到过类似的问题,想了很多办法来解决,结果还是无果。看了你的文章恍然大悟,原来方法这么简单啊!
有7位网友表示赞同!
爱你的小笨蛋
说的是事实,不同程序在同一个端口下运行是存在问题的!感觉这个技术细节需要特别注重,否则容易出现一些莫名其妙的 bug ,很感谢分享这些经验!
有19位网友表示赞同!
病房
对于我来说这个知识点比较难理解,尤其是描述的一些socket概念。希望以后有更详细的讲解,以便我能更好地掌握这门技术啊!
有16位网友表示赞同!
如梦初醒
同一个端口使用不同的socket确实可行,关键在于如何有效区分它们。文章说得挺清楚,但实践起来可能还是需要更多的代码练习和调试才能完全理解。
有9位网友表示赞同!
稳妥
太赞了!之前一直没明白如何实现同一端口多套服务的功能,现在明白了这个原理后,感觉自己的视野开阔了好多。 真是受益良多!
有18位网友表示赞同!
你的眸中有星辰
我更喜欢使用其他的网络通信技术,比如 TCP/UDP,比较简洁高效嘛。这个 socket 的区分技术确实很巧妙,但对于我的需求来说可能不太实用.
有7位网友表示赞同!
南宫沐风
写得真不错!尤其对socket的理解和描述很棒,清晰易懂。让我一下子就明白了如何利用 port 和地址信息来区分不同的 socket 。感谢分享!
有19位网友表示赞同!
海盟山誓总是赊
我觉得对于初学者来说,这篇文章有些过于专业了。希望文章中能加入更多实例和图示说明,更容易让人理解。
有9位网友表示赞同!
浮世繁华
不同socket在同一个端口下运行,确实会带来很多麻烦!比如程序冲突、数据混乱等等。一定要注意安全性和稳定性,建议在实际应用中尽量避免这样的情况发生。
有6位网友表示赞同!
孤岛晴空
文章内容丰富细致,讲解也很到位,对我学习非常有帮助!让我对同一端口如何区分不同的socket有了更深入的理解
有19位网友表示赞同!
鹿先森,教魔方
我目前主要用python开发项目。知道这篇文章分享的内容很厉害,但是我觉得对于我的项目的场景使用范围局限性还是比较大的。
有14位网友表示赞同!
瑾澜
这个知识点非常重要!一定要掌握好才能写出高效稳定的网络程序哦~
有18位网友表示赞同!
雁過藍天
学习socket确实是一件很有挑战的事情啊!希望以后能有更多关于socket的实战教程,这样才能更好地运用到实际项目中。
有20位网友表示赞同!
景忧丶枫涩帘淞幕雨
我发现一些应用软件明明使用了不同的端口号,但还是出现了类似端口冲突的情况。这让我感到困惑,难道这种方法无法解决所有问题?
有19位网友表示赞同!
抚涟i
同一个端口区分socket确实是非常有价值的,希望更多的开发者能够学习和应用到他们的项目中,从而构建更加健壮稳定的网络系统!
有6位网友表示赞同!
哽咽
这个知识点我之前一直没有深入了解过,看了你的文章受益匪浅!原来还能用这种方法来解决同一端口下不同socket的问题。真是开眼界啊!
有9位网友表示赞同!
秒淘你心窝
对于开发人员来说,理解同一端口如何区分不同的socket非常重要。 这篇文章讲解很到位,帮助我拓宽了视野。感谢分享!
有13位网友表示赞同!