概述
套接字的五元组{protocol,src_addr,src_port,dest_addr,dest_port}
五元组很重要啊!!!
https://blog.csdn.net/myhes/article/details/108318908
如何确定一个链接?或者说系统如何区分这么多链接(10k)?五元组唯一确定一个链接。
下面说说几个函数
socket函数
#include <sys/socket.h>
int socket(int family, int type, int protocol);//成功返回非负数,失败返回-1
//tcp family=AF_INET, type=SOCK_STREAM, protocol=IPPROTO_TCP/0
//udp family=AF_INET, type=SOCK_DGRAM, protocol=IPPROTO_UDP/0
//其他组合可参考《网络编程 卷1:套接字联网API》
connect函数
#include <sys/socket.h>
int connect(int sockfd,const struct sockaddr* servaddr,socklen_t addrlen);//成功0,失败-1
对于tcp而言,connect包含了:三次握手 + setpeername,只能调用一次。
对于udp而言,connect包含了:setpeername,可以调用多次。
对udp调用connect之后,只能改用send/recv。在性能上connect后调用send/recv比sendto/recvfrom更高。
getsockname和getpeername函数
#include <sys/socket.h>
int getsockname(int sockfd,struct sockaddr* localaddr,socklen_t *addrlen);//成功0,失败-1
int getpeername(int sockfd,struct sockaddr* peeraddr,socklen_t *addrlen);//成功0,失败-1
getsockname用法:
1、在一个没有调用bind的tcp客户端上,connect成功后,调用getsockname获取内核赋予该链接的本地IP地址和端口。
2、tcp客户端以端口号0(或者指定端口)调用bind后,调用getsockname获取内核赋予该链接的本地IP地址和端口。
getpeername用法:
1、tcp客户端connect连接成功后,调用getpeername获取服务端的IP地址和端口。
2、tcp服务端accept成功后,调用getpeername获取客户端的IP地址和端口。
tcp_server.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
void show(int sockfd,char* sockname){
//getsockname(sockfd,(struct sockaddr*)&client_addr,&client_addr_len)
//getpeername(sockfd,(struct sockaddr*)&server_addr,&server_addr_len)
}
int main(){
int server_sockfd,client_sockfd;
server_sockfd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in client_addr,server_addr;
socklen_t client_addr_len = sizeof(client_addr),server_addr_len = sizeof(server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9999);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(server_sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0){
printf("%d %d %sn",__LINE__,errno,strerror(errno));
}
show(server_sockfd,"server");
//server getsockname 127.0.0.1:9999
//server getpeername fail 107 Transport endpoint is not connected
listen(server_sockfd,5);
client_sockfd = accept(server_sockfd,(struct sockaddr*)&client_addr,&client_addr_len) ;
show(server_sockfd,"server");
//server getsockname 127.0.0.1:9999
//server getpeername fail 107 Transport endpoint is not connected
show(client_sockfd,"client");
//client getsockname 127.0.0.1:9999
//client getpeername 127.0.0.1:7777
char sendData[1024]={0},recvData[1024]={0};
int sendLen,recvLen;
recvLen = recv(client_sockfd,recvData,sizeof(recvData),0);
printf("%d %sn",recvLen,recvData);
sprintf(sendData,"hello client");
sendLen = send(client_sockfd,sendData,strlen(sendData),0);
printf("%d %sn",sendLen,sendData);
close(client_sockfd);
close(server_sockfd);
}
tcp_client.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
void show(int sockfd,char* sockname){
//getsockname(sockfd,(struct sockaddr*)&client_addr,&client_addr_len)
//getpeername(sockfd,(struct sockaddr*)&server_addr,&server_addr_len)
}
int main(){
int sockfd;
sockfd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in client_addr,server_addr;
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(7777);//客户端绑定7777端口
client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd,(struct sockaddr*)&client_addr,sizeof(client_addr)) < 0){
printf("%d %d %sn",__LINE__,errno,strerror(errno));
}
show(sockfd,"client");
//client getsockname 0.0.0.0:7777
//client getpeername fail 107 Transport endpoint is not connected
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9999);
inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr);
if(connect(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0){
printf("%d %d %sn",__LINE__,errno,strerror(errno));
}
show(sockfd,"client");
//client getsockname 127.0.0.1:7777
//client getpeername 127.0.0.1:9999
char sendData[1024]={0},recvData[1024]={0};
int sendLen,recvLen;
sprintf(sendData,"hello server");
sendLen = send(sockfd,sendData,strlen(sendData),0);
printf("%d %sn",sendLen,sendData);
recvLen = recv(sockfd,recvData,sizeof(recvData),0);
printf("%d %sn",recvLen,recvData);
close(sockfd);
}
总结一下,tcp的client在connect后是得到,五元组齐全了;tcp的server在accept后,server的socket还是原来那样,但是返回的socket是五元组齐全的。
接下来是udp的情况
udp_server.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
void show(int sockfd,char* sockname){
//getsockname(sockfd,(struct sockaddr*)&client_addr,&client_addr_len)
//getpeername(sockfd,(struct sockaddr*)&server_addr,&server_addr_len)
}
int main(){
int server_sockfd,client_sockfd;
server_sockfd = socket(AF_INET,SOCK_DGRAM,0);
struct sockaddr_in client_addr,server_addr;
socklen_t client_addr_len = sizeof(client_addr),server_addr_len = sizeof(server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9999);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(server_sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0){
printf("%d %d %sn",__LINE__,errno,strerror(errno));
}
show(server_sockfd,"server");
//server getsockname 0.0.0.0:9999
//server getpeername fail 107 Transport endpoint is not connected
char sendData[1024]={0},recvData[1024]={0};
int sendLen,recvLen;
recvLen = recvfrom(server_sockfd,recvData,0,0,(struct sockaddr*)&client_addr,&client_addr_len) ;
show(server_sockfd,"server");
//server getsockname 0.0.0.0:9999
//server getpeername fail 107 Transport endpoint is not connected
int server_sockfd2 = socket(AF_INET,SOCK_DGRAM,0);
struct sockaddr_in server_addr2;
server_addr2.sin_family = AF_INET;
server_addr2.sin_port = htons(10000);
server_addr2.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(server_sockfd2,(struct sockaddr*)&server_addr2,sizeof(server_addr2)) < 0){
printf("%d %d %sn",__LINE__,errno,strerror(errno));
}
sendLen = sendto(server_sockfd2,sendData,0,0,(struct sockaddr*)&client_addr,client_addr_len);
show(server_sockfd2,"server2");
//server2 getsockname 0.0.0.0:10000
//server2 getpeername fail 107 Transport endpoint is not connected
if(connect(server_sockfd2,(struct sockaddr*)&client_addr,client_addr_len) < 0){
printf("%d %d %sn",__LINE__,errno,strerror(errno));
}
show(server_sockfd2,"server2");
//server2 getsockname 127.0.0.1:10000
//server2 getpeername 127.0.0.1:7777
recvLen = recv(server_sockfd2,recvData,sizeof(recvData),0);
printf("%d %sn",recvLen,recvData);
sprintf(sendData,"hello client");
sendLen = send(server_sockfd2,sendData,strlen(sendData),0);
printf("%d %sn",sendLen,sendData);
close(server_sockfd2);
close(server_sockfd);
}
udp_client.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
void show(int sockfd,char* sockname){
//getsockname(sockfd,(struct sockaddr*)&client_addr,&client_addr_len)
//getpeername(sockfd,(struct sockaddr*)&server_addr,&server_addr_len)
}
int main(){
int sockfd;
sockfd = socket(AF_INET,SOCK_DGRAM,0);
struct sockaddr_in client_addr,server_addr;
socklen_t client_addr_len=sizeof(client_addr),server_addr_len=sizeof(server_addr);
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(7777);
client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd,(struct sockaddr*)&client_addr,sizeof(client_addr)) < 0){
printf("%d %d %sn",__LINE__,errno,strerror(errno));
}
show(sockfd,"client");
//client getsockname 0.0.0.0:7777
//client getpeername fail 107 Transport endpoint is not connected
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9999);
inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr);
char sendData[1024]={0},recvData[1024]={0};
int sendLen,recvLen;
sendLen = sendto(sockfd,sendData,0,0,(struct sockaddr*)&server_addr,sizeof(server_addr));
struct sockaddr_in server_addr2;
int server_addr_len2 = sizeof(server_addr2);
recvfrom(sockfd,recvData,0,0,(struct sockaddr*)&server_addr2,&server_addr_len2);
show(sockfd,"client");
//client getsockname 0.0.0.0:7777
//client getpeername fail 107 Transport endpoint is not connected
if(connect(sockfd,(struct sockaddr*)&server_addr2,server_addr_len2) < 0){
printf("%d %d %sn",__LINE__,errno,strerror(errno));
}
show(sockfd,"client");
//client getsockname 127.0.0.1:7777
//client getpeername 127.0.0.1:10000
sprintf(sendData,"hello server");
sendLen = send(sockfd,sendData,strlen(sendData),0);
printf("%d %sn",sendLen,sendData);
recvLen = recv(sockfd,recvData,sizeof(recvData),0);
printf("%d %sn",recvLen,recvData);
close(sockfd);
}
SO_REUSEADDR
#include <sys/socket.h>
int flag = 1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(void*)&flag,sizeof(flag));
《网络编程 卷1:套接字联网API》书上这么写的
1、SO_REUSEADDR允许启动一个监听服务器并捆绑其总所周知的端口,即使以前建立的该端口用作它们的本地端口的链接仍存在。
2、SO_REUSEADDR允许在同一个端口上启动同一个服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。
3、SO_REUSEADDR允许单个进程捆绑同一个端口到多个套接字上,只要每次捆绑指定不同的本地IP地址即可。
4、SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口已绑定到某个套接字上市,如果传输协议支持,同样的IP地址和端口还可以捆绑到另一个套接字上。
https://www.jianshu.com/p/a23b7e8a4c6a
什么意思?上代码
多个相同的client
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
int main(){
int sockfd,sockfd2;
int flag = 1;
struct sockaddr_in client_addr;
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(22222);
client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
sockfd = socket(AF_INET,SOCK_STREAM,0);
//如果不加,就会出现bind失败
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(void*)&flag,sizeof(flag));
if(bind(sockfd,(struct sockaddr*)&client_addr,sizeof(client_addr)) < 0){
printf("%d %d %sn",__LINE__,errno,strerror(errno));
}
sockfd2 = socket(AF_INET,SOCK_STREAM,0);
//如果不加,就会出现bind失败
setsockopt(sockfd2,SOL_SOCKET,SO_REUSEADDR,(void*)&flag,sizeof(flag));
if(bind(sockfd2,(struct sockaddr*)&client_addr,sizeof(client_addr)) < 0){
printf("%d %d %sn",__LINE__,errno,strerror(errno));
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9999);
inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr);
if(connect(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0){
printf("sockfd connect fail %d %sn",errno,strerror(errno));
}
//第二个sockfd2,connect失败
if(connect(sockfd2,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0){
printf("sockfd2 connect fail %d %sn",errno,strerror(errno));
}
close(sockfd);
close(sockfd2);
}
client,相同本地port+ip(SO_REUSEADDR)连接相同的目的port+ip会失败
多个相同的server
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
int main(){
int sockfd,sockfd2;
int flag = 1;
struct sockaddr_in client_addr;
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(22222);
client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
sockfd = socket(AF_INET,SOCK_STREAM,0);
//如果不加,就会出现bind失败
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(void*)&flag,sizeof(flag));
if(bind(sockfd,(struct sockaddr*)&client_addr,sizeof(client_addr)) < 0){
printf("%d %d %sn",__LINE__,errno,strerror(errno));
}
sockfd2 = socket(AF_INET,SOCK_STREAM,0);
//如果不加,就会出现bind失败
setsockopt(sockfd2,SOL_SOCKET,SO_REUSEADDR,(void*)&flag,sizeof(flag));
if(bind(sockfd2,(struct sockaddr*)&client_addr,sizeof(client_addr)) < 0){
printf("%d %d %sn",__LINE__,errno,strerror(errno));
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9999);
inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr);
if(listen(sockfd,5) < 0){
printf("sockfd listen fail %d %sn",errno,strerror(errno));
}
//第二个sockfd2,listen失败
if(listen(sockfd2,5) < 0){
printf("sockfd2 listen fail %d %sn",errno,strerror(errno));
}
close(sockfd);
close(sockfd2);
}
server,相同本地port+ip(SO_REUSEADDR)listen失败。(listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应接受指向该套接字的连接请求)
总结一下就是
tcp server,listen后,占用内核资源,唯一。
tcp client,connect建立连接后,由五元组判断唯一。
因此这种情况成立,tcp server和tcp client占用相同的本地端口和ip -->tcp nat穿透
udp不存在以上两种情况 --> udp nat穿透
了解清楚后,下一节预告:nat穿透
SO_REUSERADDR和SO_REUSEPORT的讨论
https://stackoverflow.com/questions/14388706/how-do-so-reuseaddr-and-so-reuseport-differ
最后
以上就是苹果糖豆为你收集整理的网络编程学习1:套接字socket的全部内容,希望文章能够帮你解决网络编程学习1:套接字socket所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复