unix socket interface

socket

Create a socket file:

#include <sys/socket.h>

// 成功返回非负套接字描述符,失败返回-1
int socket(int domain, int type, int protocol);

domain value:

domain description
AF_INET IPv4 Internet protocols
AF_INET6 IPv6 Internet protocols

type value:

type description
SOCK_STREAM Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported.
SOCK_DGRAM Supports datagrams (connectionless, unreliable messages of a fixed maximum length).

protocol value:

protocol description
IPPROTO_TCP TCP protocol
IPPROTO_UDP UDP protocol

There is no macro defined in socket, so additional introduction is needed<netinet/in.h>

sockaddr

sockaddr is used to record ip and port information. sockaddr is a general structure. There is no specific information field for dividing ip and port. For ipv4 address, sockaddr_in structure is needed. For ipv6, sockaddr_in6 structure is needed:

<sys/socket.h>

// 通用地址信息结构体
struct sockaddr {
    sa_family_t sa_family;      // 地址簇
    char sa_data[14];           // 填充字符
};

<netinet/in.h>

// 存储ipv4地址
struct in_addr {
    in_addr_t s_addr;           // ipv4地址(网络序4字节)
};

// ipv4地址和端口信息
struct sockaddr_in {
    sa_family_t sin_family;     // AF_INET
    in_port_t sin_port;         // 端口号(网络序2字节)
    struct in_addr sin_addr;    // ipv4地址
    char sin_zero[8];           // 填充字符
};

// 存储ipv6地址
struct in6_addr {
    uint8_t s6_addr[16];        // ipv6地址(网络序16字节)
};

// ipv6地址和端口信息
struct sockaddr_in6 {
    sa_family_t sin6_family;    // AF_INET6
    in_port_t sin6_port;        // 端口号(网络序2字节)
    uint32_t sin6_flowinfo;     // 流信息
    struct in6_addr sin6_addr;  // ipv6地址
    uint32_t sin6_scope_id;     // 作用域的接口集合
};

Before using the sockaddr_in structure, you need to clear the entire structure.
Inet_pton can be used to convert the string to the ip address type, and the conversion returns 1:

<netinet/in.h>

// 转换成功返回1,ip字符串不合法返回0,地址簇不支持返回-1
int inet_pton(int af, const char *src, void *dst);

In addition to converting the IP address through a string, the <netinet/in.h>header file is defined INADDR_ANYto represent 0.0.0.0this wildcard address.

bind

Bind the socket to the specified ip address and port:

#include <sys/socket.h>

// 失败返回-1,成功返回0
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);

For the server, if you want to listen to multiple network cards at the same time, you need to bind to INADDR_ANYit. If you only accept local access, you need to bind to 127.0.0.1it.

listen

The socket descriptor state from the server CLOSEDproceeds to LISTEN:

#include <sys/socket.h>

// 失败返回-1,成功返回0
int listen(int sockfd, int backlog);

backlogIndicates the size of the connection queue. Although it is not specified in the standard, in the implementation of BSD4.2, the connection queue includes two state connections:

  • Some connections have just received a SYN packet and are in a SYN_RCVDstate.
  • Some connections have completed the three-way handshake process and are in a ESTABLISHEDstate

You cannot set backlog to 0, because this behavior standard is not defined, if you do not want the client to be able to connect to yourself, then you need to close the listening socket. And the size of the actual connection queue is not necessarily equal backlog, for example, some implementations will be multiplied by a factor of 1.5.

accept

Used to accept connections in a ESTABLISHEDstate in the connection queue :

#include <sys/socket.h>

// 成功返回非负的连接套接字描述符,失败返回-1
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

cliaddr can be used to obtain the client's address and port information.

#include <arpa/inet.h>

// 转换成功返回dst指针,转换失败返回NULL
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

<netinet/in.h>The header file defines the length of the character array required to save the ipv4 address and ipv6 string:

#define INET_ADDRSTRLEN 16  /* for IPv4 dotted-decimal */
#define INET6_ADDRSTRLEN 46 /* for IPv6 hex string */

connect

The client is used to connect to the server:

#include <sys/socket.h>

// 失败返回-1,成功返回0
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

When the client calls this interface, it starts a TCP three-way handshake, and first sends a SYN packet, if:

  • If no reply has been received, the timeout mechanism (75 seconds) will be triggered. During this period, the SYNC packet will be retransmitted continuously until the ETIMEOUT error is returned.
  • If the service host returns RST (reset), it means that there is no service listening on the port on the service host, then the client returns an ECONNREFUSED error
  • If an ICMP packet is returned, indicating that the network or host is unreachable, then a timeout mechanism will still be triggered, during which SYNC packets will be retransmitted continuously until an EHOSTUNREACH error is returned

Client code

#include <iostream>
#include <string>
#include <cstring>          // memset strlen
#include <cstdint>          // uint16_t
#include <cstdio>           // snprintf
#include <sys/socket.h>     // socket
#include <netinet/in.h>     // IPPROTO_TCP htons
#include <arpa/inet.h>      // inet_pton
#include <unistd.h>         // write read

int main(int argc, char const *argv[])
{
    // 创建连接套接字
    int connectfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (-1 == connectfd) {
        std::cout << "create connectfd failed" << std::endl;
        return -1;
    }
    std::cout << "connectfd: " << connectfd << std::endl;

    // 设置服务器地址(ipv4)
    sockaddr_in servAddr;
    std::string ipAddr = "127.0.0.1";
    uint16_t portNum = 23333;
    memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_port = htons(portNum);
    int res = inet_pton(AF_INET, ipAddr.c_str(), &servAddr.sin_addr);
    if (1 != res) {
        std::cout << "presentation(" << ipAddr << ") to numeric failed" << std::endl;
        return -1;
    }

    // 连接服务器
    res = connect(connectfd, reinterpret_cast<sockaddr *>(&servAddr), sizeof(servAddr));
    if (-1 == res) {
        std::cout << "connect server failed" << std::endl;
        return -1;
    }

    // 发送消息
    constexpr int MAX = 1024;
    char sendBuf[MAX + 1];
    snprintf(sendBuf, sizeof(sendBuf), "hello");
    write(connectfd, sendBuf, strlen(sendBuf));

    // 接受回执
    char recvBuf[MAX + 1];
    read(connectfd, recvBuf, sizeof(recvBuf));
    std::cout << recvBuf << std::endl;

    // 关闭套接字
    close(connectfd);

    return 0;
}

Server code:

#include <iostream>
#include <string>
#include <cstring>          // memset
#include <cstdint>          // uint16_t
#include <sys/socket.h>     // socket
#include <netinet/in.h>     // IPPROTO_TCP htons ntohs INET_ADDRSTRLEN
#include <arpa/inet.h>      // inet_pton inet_ntop
#include <unistd.h>         // write read

int main(int argc, char const *argv[])
{
    // 创建监听socket
    int listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (-1 == listenfd) {
        std::cout << "create listenfd failed" << std::endl;
        return -1;
    }

    std::cout << "listenfd: " << listenfd << std::endl;

    // 设置服务器地址(ipv4)
    sockaddr_in servAddr;
    std::string ipAddr = "0.0.0.0";
    uint16_t portNum = 23333;
    memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_port = htons(portNum);
    int res = inet_pton(AF_INET, ipAddr.c_str(), &servAddr.sin_addr);
    if (1 != res) {
        std::cout << "presentation(" << ipAddr << ") to numeric failed" << std::endl;
        return -1;
    }

    std::cout << "set ipv4 address and port number success" << std::endl;

    // 绑定服务器地址
    res = bind(listenfd, reinterpret_cast<sockaddr*>(&servAddr), sizeof(servAddr));
    if (0 != res) {
        std::cout << "bind listenfd failed" << std::endl;
        return -1;
    }

    std::cout << "bind listenfd success" << std::endl;

    // 套接字开启监听
    res = listen(listenfd, 5);
    if (0 != res) {
        std::cout << "listen listenfd failed" << std::endl;
        return -1;
    }

    std::cout << "listen listenfd success" << std::endl;

    // 服务循环
    while (true) {
        // 接受远端连接
        sockaddr_in clientAddr;
        socklen_t len = sizeof(clientAddr);
        int connectfd = accept(listenfd, reinterpret_cast<sockaddr*>(&clientAddr), &len);
        if (-1 == connectfd) {
            std::cout << "accept connectfd failed" << std::endl;
            return -1;
        }
        char clientIpAddr[INET_ADDRSTRLEN];
        uint16_t clientPortNum = ntohs(clientAddr.sin_port);
        inet_ntop(AF_INET, &clientAddr.sin_addr, clientIpAddr, sizeof(clientIpAddr));
        std::cout << "accept connectfd(" << clientIpAddr << ":" << clientPortNum << ") success" << std::endl;

        // 接受消息
        constexpr int MAX = 1024;
        char recvBuf[MAX + 1];
        read(connectfd, recvBuf, sizeof(recvBuf));

        // echo消息
        char sendBuf[MAX + 1];
        snprintf(sendBuf, sizeof(sendBuf), "%s", recvBuf);
        write(connectfd, sendBuf, strlen(sendBuf));

        // 本轮服务结束,关闭连接套接字
        close(connectfd);
    }

    return 0;
}

Guess you like

Origin www.cnblogs.com/HachikoT/p/12683352.html