我是靠谱客的博主 苹果糖豆,这篇文章主要介绍网络编程学习1:套接字socket,现在分享给大家,希望可以做个参考。

套接字的五元组{protocol,src_addr,src_port,dest_addr,dest_port}

五元组很重要啊!!!

https://blog.csdn.net/myhes/article/details/108318908

如何确定一个链接?或者说系统如何区分这么多链接(10k)?五元组唯一确定一个链接。

 

下面说说几个函数

socket函数

复制代码
1
2
3
4
5
#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函数

复制代码
1
2
#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函数

复制代码
1
2
3
4
#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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#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

复制代码
1
2
3
4
#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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#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内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部