美文网首页
socket编程总结

socket编程总结

作者: 客昂康 | 来源:发表于2021-05-06 18:23 被阅读0次

1. socket() 创建一个套接字

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

domain: 通信域,又叫协议族,通常有下列这些:

协议族 含义
AF_UNIX、AF_LOCAL、AF_FILE UNIX域,又叫本地域或文件域。
AF_INET IPv4协议。
AF_INET6 IPv6协议。
AF_IPX IPX协议。
AF_NETLINK 用于内核和用户的Netlink通信。
AF_PACKET 原始套接字,低层数据包接口。
... ...

type: socket类型,有下列这些:

socket类型 含义
SOCK_STREAM 流式套接字,特点是有连接、双向传输、传输可靠、有顺序无边界、支持带外数据。
SOCK_DGRAM 报文套接字,特点是无连接、单向传输、传输不可靠、包大小有限、到达顺序不确定。
SOCK_SEQPACKET 序列包套接字,特点是有连接,双向传输、传输可靠、有顺序,包大小固定。
SOCK_RAW 提供原始网络协议访问。
SOCK_RDM 提供一个不保证排序的可靠数据报层。

另外type支持按位或 SOCK_NONBLOCKSOCK_CLOEXECSOCK_NONBLOCK可以设置 O_NONBLOCK状态,见fcntl()SOCK_CLOEXEC可以设置FD_CLOEXEC状态,见open()O_CLOEXEC

protocol: 指定协议。确定domaintype后通常只有一个协议与之对应,此时protocol设置为0即可。


2. socketpair() 创建一对互通的套接字

#include <sys/types.h>
#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);

domaintypeprotocolsocket() 的参数含义相同,这里的 domain 只能是 UNIX 域,sv[0]sv[1] 是被创建的一对可互相通信的套接字。


3. bind() 将套接字与地址绑定

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockfd: 要绑定地址的套接字。
addr: 指向地址结构体,如下所示:

struct sockaddr {
    sa_family_t  sa_family;
    char         sa_data[14];
}

但是此结构体的唯一作用是强制转换addr中传递的指针,以避免编译器警告。而真正使用的结构体,随协议族的不同而不同,举例如下:

UNIX域使用struct sockaddr_un结构体地址。

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, "abcdefghijklmnop");

bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un));

IPv4使用struct sockaddr_in结构体地址。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in addr;
addr.sin_family      = AF_INET;
addr.sin_port        = htons(8080);
addr.sin_addr.s_addr = inet_addr("0.0.0.0");

bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in));

IPv6使用struct sockaddr_in6结构体地址。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int sockfd = socket(AF_INET6, SOCK_STREAM, 0);

struct sockaddr_in6 addr;
addr.sin6_family = AF_INET6;
addr.sin6_port   = htons(8080);
inet_pton(AF_INET6, "::1", &addr.sin6_addr);

bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in6));

addrlen: addr 所指结构体的大小。

bind() 的作用是让另一个套接字找到这个套接字,所以 bind() 只对 socket() 创建的套接字有意义,socketpair() 创建的一对套接字已经互通,不需要bind()


4. listen() 将套接字设置为被动监听状态

#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);

sockfd: 要被设置为监听的套接字。
backlog: 监听队列最大长度。
listen() 仅用于面向连接的套接字,即 typeSOCK_STREAMSOCK_SEQPACKET 的套接字。


5. accept() 在监听套接字上等待新连接

#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd: 监听中的套接字。该套接字一定是由socket()创建,由bind()绑定了地址,由listen()设置为监听状态。
addr: 指向新的连接的对方的地址。
addrlen: addr 所指结构体的大小。
accept() 返回新的套接字,且 accept() 仅用于面向连接的套接字,即 typeSOCK_STREAMSOCK_SEQPACKET 的套接字。

#include <sys/types.h>
#define _GNU_SOURCE
#include <sys/socket.h>
int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);

accept4()accept() 多了一个参数 flages
flags 等于 0 时,accept4() 等同于 accept()
flags 包含 SOCK_NONBLOCK 时,accept4() 返回的新套接字包含 O_NONBLOCK 属性。
flags 包含 SOCK_CLOEXEC 时,accept4() 返回的新套接字包含 FD_CLOEXEC 属性。


6. connect() 发起连接

#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockfd: 发起连接的套接字。
addr: 等待连接的地址。
addrlen: addr 所指结构体的大小。
如果 sockfdSOCK_DGRAM 套接字,则 addr 是发送数据报时默认到达地址,也是接收数据报时唯一来源地址。
如果 sockfdSOCK_STREAMSOCK_SEQPACKET 套接字,则 connect()sockfdaddr 建立连接。


7. send()、sendto()、sendmsg() 发送数据

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

8. recv()、recvfrom()、recvmsg() 接收数据

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

9. getsockopt() 获取套接字选项

#include <sys/types.h>
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);

10. setsockopt() 设置套接字选项

#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

11. getpeername() 获取套接字对端地址

#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

12. getsockname() 获取套接字本地地址

#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

13. shutdown() 关闭套接字的连接

#include <sys/socket.h>
int shutdown(int sockfd, int how);

sockfd: 要被关闭的套接字。
how: 如何关闭套接字。关闭方式如下:
SHUT_RD 禁用进一步的接收操作。
SHUT_WR 禁用进一步的发送操作。
SHUT_RDWR 禁用进一步的发送和接收操作。
注意 shutdown() 仅仅关闭套接字的连接,没有释放套接字所占的文件描述符,进一步释放文件描述符使用 close()


14. 常用代码片段

#include <unistd.h>      // close()
#include <sys/types.h>   // 
#include <sys/socket.h>  // 
#include <sys/un.h>      // unix域
#include <netinet/in.h>  // IPv4 IPv6

// 创建unix域TCP协议的socket
int createSocket_unix_tcp(char *path){
    // 创建数据流式socket
    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if(sockfd < 0){
        return -1;
    }
    
    // 设置地址协议族为unix域,并设置path为虚拟path。
    struct sockaddr_un addr;
    int pathLen = strlen(path);
    if(pathLen > sizeof(addr.sun_path)-2){
        pathLen = sizeof(addr.sun_path)-2;
    }
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    addr.sun_path[0] = 0;
    memcpy(addr.sun_path+1, path, pathLen);
    
    // 绑定地址
    if(bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)) < 0){
        close(sockfd);
        return -2;
    }
    
    // 设置为监听
    if(listen(sockfd, 4) < 0){
        close(sockfd);
        return -3;
    }
    
    return sockfd;
}

// 创建IPv4协议族TCP协议的socket
int createSocket_ipv4_tcp(char *ipv4, int port){
    // 创建数据流式socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        return -1;
    }
    
    // 设置IPv4协议族地址
    struct sockaddr_in addr;
    addr.sin_family  = AF_INET;
    addr.sin_port      = htons(port);
    addr.sin_addr.s_addr = inet_addr(ipv4);
    
    // 绑定地址
    if(bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0){
        close(sockfd);
        return -2;
    }
    
    // 设置为监听
    if(listen(sockfd, 4) < 0){
        close(sockfd);
        return -3;
    }
    
    return sockfd;
}

// 创建IPv6协议族TCP协议的socket
int createSocket_ipv6_tcp(char *ipv6, int port){
    // 创建数据流式socket
    int sockfd = socket(AF_INET6, SOCK_STREAM, 0);
    if(sockfd < 0){
        return -1;
    }
    
    // 设置IPv6协议族地址
    struct sockaddr_in6 addr;
    addr.sin6_family = AF_INET6;
    addr.sin6_port   = htons(port);
    inet_pton(AF_INET6, ipv6, &addr.sin6_addr);
    
    // 绑定地址
    if(bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in6)) < 0){
        close(sockfd);
        return -2;
    }
    
    // 设置为监听
    if(listen(sockfd, 4) < 0){
        close(sockfd);
        return -3;
    }
    
    return sockfd;
}

// 等待新的TCP连接, unix域.
int waitNewSocket_unix_tcp(int sockfd){
    struct sockaddr_un addr;
    socklen_t size = sizeof(struct sockaddr_un);
    return accept(sockfd, (struct sockaddr*)&addr, &size);
}

// 等待新的TCP连接, IPv4.
int waitNewSocket_ipv4_tcp(int sockfd){
    struct sockaddr_in addr;
    socklen_t size = sizeof(struct sockaddr_in);
    return accept(sockfd, (struct sockaddr*)&addr, &size);
}

// 等待新的TCP连接, IPv6.
int waitNewSocket_ipv6_tcp(int sockfd){
    struct sockaddr_in6 addr;
    socklen_t size = sizeof(struct sockaddr_in6);
    return accept(sockfd, (struct sockaddr*)&addr, &size);
}

// 用法伪代码
void usage_unix_tcp(void){
    char buffer[256];
    int listenFd, newFd, size;
    
    listenFd = createSocket_unix_tcp("abcd1234");
    
    for(;;){
        newFd = waitNewSocket_unix_tcp(listenFd);
        
        size = recv(newFd, buffer, 256, 0);
        
        send(newFd, buffer, size, 0);
        
        close(newFd);
    }
    
    close(listenFd);
}

// 用法伪代码
void usage_ipv4_tcp(void){
    char buffer[256];
    int listenFd, newFd, size;
    
    listenFd = createSocket_ipv4_tcp("0.0.0.0", 2345);
    
    for(;;){
        newFd = waitNewSocket_ipv4_tcp(listenFd);
        
        size = recv(newFd, buffer, 256, 0);
        
        send(newFd, buffer, size, 0);
        
        close(newFd);
    }
    
    close(listenFd);
}

// 用法伪代码
void usage_ipv6_tcp(void){
    char buffer[256];
    int listenFd, newFd, size;
    
    listenFd = createSocket_ipv6_tcp("0:0:0:0:0:0:0:0", 2345);
    
    for(;;){
        newFd = waitNewSocket_ipv6_tcp(listenFd);
        
        size = recv(newFd, buffer, 256, 0);
        
        send(newFd, buffer, size, 0);
        
        close(newFd);
    }
    
    close(listenFd);
}

////////////////////////////////////////////////////////////////////////////////////////////////////

// 创建Unix域UDP协议的socket
int createSocket_unix_udp(char *path){
    // 创建数据报式socket
    int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
    if(sockfd < 0){
        return -1;
    }
    
    // 设置地址协议族为unix域,并设置path为虚拟path。
    struct sockaddr_un addr;
    int pathLen = strlen(path);
    if(pathLen > sizeof(addr.sun_path)-2){
        pathLen = sizeof(addr.sun_path)-2;
    }
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    addr.sun_path[0] = 0;
    memcpy(addr.sun_path+1, path, pathLen);
    
    // 绑定地址
    if(bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)) < 0){
        close(sockfd);
        return -2;
    }
    
    return sockfd;
}

// 创建IPv4协议族UDP协议的socket
int createSocket_ipv4_udp(char *ipv4, int port){
    // 创建数据流式socket
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0){
        return -1;
    }
    
    // 设置IPv4协议族地址
    struct sockaddr_in addr;
    addr.sin_family  = AF_INET;
    addr.sin_port      = htons(port);
    addr.sin_addr.s_addr = inet_addr(ipv4);
    
    // 绑定地址
    if(bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0){
        close(sockfd);
        return -2;
    }
    
    return sockfd;
}

// 创建IPv6协议族UDP协议的socket
int createSocket_ipv6_udp(char *ipv6, int port){
    // 创建数据流式socket
    int sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
    if(sockfd < 0){
        return -1;
    }
    
    // 设置IPv6协议族地址
    struct sockaddr_in6 addr;
    addr.sin6_family = AF_INET6;
    addr.sin6_port   = htons(port);
    inet_pton(AF_INET6, ipv6, &addr.sin6_addr);
    
    // 绑定地址
    if(bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in6)) < 0){
        close(sockfd);
        return -2;
    }
    
    return sockfd;
}

// 用法伪代码
void usage_unix_udp(void){
    char buffer[256];
    int sockfd, size;
    
    sockfd = createSocket_unix_udp("abcd1234");
    
    for(;;){
        size = recv(sockfd, buffer, 256, 0);
    }
    
    close(sockfd);
}

// 用法伪代码
void usage_ipv4_udp(void){
    char buffer[256];
    int sockfd, size;
    
    sockfd = createSocket_ipv4_udp("0.0.0.0", 2345);
    
    for(;;){
        size = recv(sockfd, buffer, 256, 0);
    }
    
    close(sockfd);
}

// 用法伪代码
void usage_ipv6_udp(void){
    char buffer[256];
    int sockfd, size;
    
    sockfd = createSocket_ipv6_udp("0:0:0:0:0:0:0:0", 2345);
    
    for(;;){
        size = recv(sockfd, buffer, 256, 0);
    }
    
    close(sockfd);
}

相关文章

  • 许世伟的Go语言基础 第五章总结

    第5章 网络编程 5.1 socket编程 以往socket编程: 建立socket:使用socket()函数。 ...

  • socket编程总结

    “一切皆Socket!”话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket。 本文内容部分转载自:...

  • socket编程总结

    1. socket() 创建一个套接字 domain: 通信域,又叫协议族,通常有下列这些: 协议族含义AF_UN...

  • 网络编程

    python学习笔记-网络编程 socket编程: socket()函数:socket.socket([famil...

  • socket浅析以及socket心跳机制

    最近在看socket编程的一些内容,想总结下。先从理论知识总结下,不涉及代码。 1.socket是什么? sock...

  • TCP socket 编程

    TCP socket 编程 讲一下 socket 编程 步骤 使用 socket 模块 建立 TCP socket...

  • 网络编程

    网络 Socket 基于TCP协议的Socket编程 基于UDP协议的Socket编程

  • 2018-09-12 day18-网络基础

    总结 一.socket编程 socket又叫套接字,就是进行数据通信两端,分为服务端套接字和客户端套接字套接字编程...

  • 网络编程

    Linux Socket编程(不限Linux) C/C++ socket编程教程:1天玩转socket通信技术 一...

  • python网络编程中常用到的函数总结

    总结一下python网络编程中常用到的函数 1 socket.getservbyname(servicename[...

网友评论

      本文标题:socket编程总结

      本文链接:https://www.haomeiwen.com/subject/zcirdltx.html