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

概述

套接字的五元组{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所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部