0
点赞
收藏
分享

微信扫一扫

Linux系统编程—套接字

爱写作的小土豆 2022-01-20 阅读 75

1. socket

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

int socket(int domain, int type, int protocol);
  • 创建套接字。
  • 成功时返回新建的套接字文件描述符;失败时返回 -1,并设置 errno
  • domain 参数指定了通信域,常用值包括:AF_INET(IPv4)、AF_INET6(IPv6)。
  • type 参数指定了通信语义,常用值包括:SOCK_STREAM(字节流)、SOCK_GRAM(数据报)。
  • 自 Linux 2.6.27 以来,type 参数还可以或运算:SOCK_NONBLOCK(非阻塞模式)、SOCK_CLOEXEC(设置 close-on-exec 标志)。
  • protocol 参数指定要使用何种协议,通常设为 0 即可。

2. bind

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

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • addr 指定的地址(大小为 addrlen 个字节)赋给 sockfd 所指向的套接字。

  • 成功时返回 0;失败时返回 -1,并设置 errno

  • 通用地址格式:

    struct sockaddr {
    	sa_family_t sa_family;
    	char        sa_data[14];
    }
    
  • IPv4 地址格式:

    struct sockaddr_in {
    	sa_family_t    sin_family; /* address family: AF_INET */
    	in_port_t      sin_port;   /* port in network byte order */
    	struct in_addr sin_addr;   /* internet address */
    };
    
    /* Internet address. */
    struct in_addr {
    	uint32_t       s_addr;     /* address in network byte order */
    };
    
  • IPv6 地址格式:

    struct sockaddr_in6 {
    	sa_family_t     sin6_family;   /* AF_INET6 */
    	in_port_t       sin6_port;     /* port number */
    	uint32_t        sin6_flowinfo; /* IPv6 flow information */
    	struct in6_addr sin6_addr;     /* IPv6 address */
    	uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */
    };
    
    struct in6_addr {
    	unsigned char   s6_addr[16];   /* IPv6 address */
    };
    

3. listen

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

int listen(int sockfd, int backlog);
  • sockfd 所指向的套接字标记为被动套接字,即后续可以通过accept() 接受到来的连接请求。
  • 成功时返回 0;失败时返回 -1,并设置 errno
  • sockfd 所指向套接字的类型需为:SOCK_STREAMSOCK_SEQPACKET
  • backlog 指定了连接等待队列的最大长度。

4. accept

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

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

int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
  • sockfd 所关联的监听套接字的连接队列中提取第一个连接,并新建一个套接字文件描述符来指向该连接(对端套接字)。
  • 成功时返回新建的套接字文件描述符;失败时返回 -1,并设置 errno
  • addr 中返回对端套接字的地址;addrlen 传入时指定 addr 所指区域的大小,返回时更新为对端套接字的地址大小。
  • 如果对对端套接字的信息不感兴趣,可将 addr, addrlen 设为 NULL
  • 如果连接队列为空,且 sockfd 为阻塞模式,则 accept() 将阻塞住。
  • 对于 accept4() 来说,如果 flags 为 0,则等价于 accept()
  • flags 可取值为:SOCK_NONBLOCK(设置新建套接字为非阻塞模式),SOCK_CLOEXEC(设置新建套接字的 close-on-exec 标志)。

5. connect

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

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd 指向的套接字连接到 addr 所指定的地址(大小为 addrlen 字节)。
  • 成功时返回 0;失败时返回 -1,并设置 errno
  • 如果 sockfd 所指套接字的类型为 SOCK_DGRAM,则 connect() 的作用是设置数据报的默认目的地址为 addr;可调用多次 connect() 。如果想解除此设置,可将 addrsa_family 设为 AF_UNSPEC
  • 如果 sockfd 所指套接字的类型为 SOCK_STREAMSOCK_SEQPACKET,则 connect() 的作用是发送一个连接请求到 addr 所绑定的套接字;通常只能调用一次 connect()

6. 套接字选项

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

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
  • 获取、设置套接字选项。
  • 成功时返回 0;失败时返回 -1,并设置 errno
  • sockfd 指定了要操作哪个套接字,level 指定了选项所处的级别。
  • optname 指定了选项,optval 指定了选项值。optlen 指定了选项值的大小;对于 getsockopt() 来说,它是一个值-结果参数,需要在调用时初始化,并在其中返回选项值的大小。
  • 具体包括哪些选项可查看手册:man 7 socketman 7 tcpman 7 udp

7. inet_pton

#include <arpa/inet.h>

int inet_pton(int af, const char *src, void *dst);
  • 将 IPv4/IPv6 地址字符串转换为二进制格式。
  • 成功时返回 1;如果传入的地址字符串格式无效,则返回 0;否则失败返回 -1,并设置 errno
  • af 指定了地址格式,可以为:AF_INETAF_INET6
  • src 指向地址字符串,dst 指向转换结果。

8. inet_ntop

#include <arpa/inet.h>

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
  • 将 二进制格式的 IPv4/IPv6 地址转换为地址字符串。
  • 成功时返回 dst,失败时返回 NULL ,并设置 errno
  • af 指定了地址格式,可以为:AF_INETAF_INET6
  • src 指向二进制格式的地址,dst 指向转换结果,size 指定了 dst 所指内存区域的大小。

9. 字节序转换

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);

uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
  • h 表示 host,即主机字节序;n 表示 network ,即网络字节序。

10. recvfrom

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

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                  struct sockaddr *src_addr, socklen_t *addrlen);
  • 通过 sockfd 指向的套接字接收数据。
  • 成功时返回接收到的字节数;失败时返回 -1,并设置 errno
  • 将接收到的数据存到 buf 中,len 指定了 buf 所指内存区域的大小。
  • src_addr 中返回对端地址,addrlen 是一个值-结果参数,需要初始化,并在返回后指明对端地址的大小。
  • flags 用于指定接收操作的选项,可将其设为 0。

11. sendto

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

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);
  • 通过 sockfd 指向的套接字发送数据。
  • 成功时返回发送的字节数;失败时返回 -1,并设置 errno
  • buf 中的数据发送出去,len 指定了 buf 所指内存区域的大小。
  • 对端地址在 dest_addr 中指定,addrlendest_addr 所指内存区域的大小。
  • flags 用于指定发送操作的选项,可将其设为 0。

12. 例子

注:为简便起见,此处省略了返回值检查。

echo server:

#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <string>

static const uint16_t Port = 6666;
static const char* Ip = "127.0.0.1";
static constexpr size_t MsgLen = 16;
static char Buf[MsgLen] = {0};

int main() {
	int listenSock = socket(AF_INET, SOCK_STREAM, 0);

    int value = 1;
    setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int));

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_port = htons(Port);
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, Ip, &addr.sin_addr);

    bind(listenSock, (const struct sockaddr*)&addr, sizeof(addr));
    listen(listenSock, 4);

    while(true) {
    	int peerSock = accept(listenSock, NULL, NULL);
    	if (peerSock == -1) {
            perror("accept()");
            continue;
        }

        ssize_t n = read(peerSock, Buf, sizeof(Buf));
        write(peerSock, Buf, n);
        close(peerSock);
    }
}

echo client:

#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <string>

static const uint16_t Port = 6666;
static const char* Ip = "127.0.0.1";
static const std::string Msg("Hello, World!");
static constexpr size_t MsgLen = 13;

int main() {
	int sock = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_port = htons(Port);
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, Ip, &addr.sin_addr);

    connect(sock, (const struct sockaddr*)&addr, sizeof(addr));

    char buf[MsgLen+1] = {0};
    ssize_t n = write(sock, Msg.c_str(), MsgLen);
    read(sock, buf, n);

    std::cout << "client: " << buf << '\n';

    close(sock);
    return 0;
}
举报

相关推荐

0 条评论