[Socket Programming] TCP server, UDP server, local socket [C language code implementation]

Table of contents

0. Prepare knowledge

0.1 Big and small endian concepts

0.2 Conversion between network byte order and host byte order

0.3 Dotted decimal string conversion (IP address conversion function)

0.4 IPV4 structure: (man 7 ip)

0.5 IPV6 socket structure: (man 7 ipv6)

0.6 Universal socket structure

1. Network socket functions

1.1 socket

1.2 connect

1.3 bind

1.4 listen

1.5 accept

1.6 Port reuse

2. Wrapping function

2.1 wrap.c

2.2 wrap.h

3.TCP server

3.1 Simple version

3.2 Multi-process version

3.3 Multi-threaded version

4. UDP server

5. Local socket

Summarize:


0. Prepare knowledge

0.1 Big and small endian concepts

大端存储模式: means that the low-endian order of the data is stored in the high address of the memory
小端存储模式 , and the high-endian order of the data is stored in the low address of the memory: means that the low-endian order of the data is stored in the low address of the memory, and the data is stored in the low-endian address of the memory. is stored in high endianness of memory at the high address of

When using different storage methods, the stored data is 0x12345678 :
Insert image description here

0.2 Conversion between network byte order and host byte order

        The TCP/IP protocol stipulates that network data flow should be in big-endian byte order , that is, low address and high byte. If the host is big-endian, no conversion is required for sending and receiving. In the same way, 32-bit IP addresses must also consider network byte order and host byte order.

        In order to make network programs portable and make the same C code run normally after compilation on big-endian and little-endian computers, you can call the following library function to convert network byte order and host byte order .

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);

uint16_t htons(uint16_t hostlshort);

uint32_t ntohl(uint32_t netlong);

uint16_t ntohs(uint16_t netshort);

h represents host, n represents network, l represents 32 bits, and s represents 16 bits.

If the host is little-endian, these functions will convert the parameters accordingly and return them. If the host is big-endian, these functions will not convert the parameters and return them unchanged.

Code example 1:

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
    char buf[4] = {
        192, 168, 1, 2
    };
    unsigned int num = *(int*)buf;
    unsigned int sum = htonl(num);
    unsigned char* p = (unsigned char*)&sum;

    printf("%d %d %d %d\n", *p, *(p + 1), *(p + 2), *(p + 3));

    unsigned short a = 0x0102;
    unsigned short b = htons(a);
    printf("%#x\n", b);

    return 0;
}

Execution screenshot:

 Code example 2:

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
    unsigned char buf[4] = {
        1, 1, 168, 192
    };
    int num = *(int*)buf;
    int sum = ntohl(num);
    unsigned char* p = (unsigned char*)&sum;

    printf("%d %d %d %d\n", *p, *(p + 1), *(p + 2), *(p + 3));

    return 0;
}

Execution screenshot:

 

0.3 Dotted decimal string conversion (IP address conversion function)

        The IP address we usually see is the string "192.168.1.2", which needs to be converted.

#include <apra/inet.h>

//Convert the dotted decimal string into 32-bit network big-endian data

int inet_pton(int af, const char *src, void *dst);

Supports IPV4 and IPV6

parameter:

        of:

                AF_INET:IPV4

                AF_INET6:IPV6

        src: address of dotted decimal string

        dst: The address where 32-bit network data is stored

return value:

        Success: 1

        failed: 0

#include <apra/inet.h>

//Convert 32-bit network big-endian data into dotted decimal string

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

parameter:

        of:

                AF_INET:IPV4

                AF_INET6:IPV6

        src: address of 32-bit network data

        dst: The address where the dotted decimal string is stored

        size: The size of the storage point decimal string array (the number of digits must be determined specifically)

                INET_ADDRSTRLEN macro value 16

return value:

        Store the first address of the dotted decimal string

Code example:

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
    char buf[] = "192.168.1.2";
    unsigned int num = 0;

    inet_pton(AF_INET, buf, &num);
    unsigned char* p = (unsigned char*)&num;
    printf("%d %d %d %d\n", *p, *(p + 1), *(p + 2), *(p + 3));

    char ip[16] = "";
    inet_ntop(AF_INET, &num, ip, 16);
    printf("%s\n", ip);

    return 0;
}

Execution screenshot:

Network communication solves three major problems: protocol, IP, and port

0.4 IPV4 structure: (man 7 ip)

 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 */
           };

0.5 IPV6 socket structure: (man 7 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 */
           };

0.6 Universal socket structure

struct sockaddr {     sa_family_t sa_family; // Address family     char sa_data[14]; // Address data };


Note: Usually the following form is used

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

1. Network socket functions

1.1 socket

#include <sys/types.h>

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

Function: Create socket

parameter:

        domain:
                AF_INET This is the protocol used by most sockets, using TCP or UDP for transmission, using the IPv4 address
                AF_INET6 is similar to the above, but using the IPv6 address
                AF_UNIX local protocol, used on Unix and Linux systems, generally
        Type: SOCK_STREAM is used when the client and server are on the same computer.
                This protocol is a sequential, reliable, data-integrated byte stream-based connection. This is the most commonly used socket type. This socket uses TCP for transmission .
                SOCK_DGRAM This protocol is a connectionless, fixed-length transfer call. This protocol is unreliable and uses UDP for its connections.
                SOCK_SEQPACKET This protocol is a dual-line, reliable connection that sends fixed-length data packets for transmission. This package must be accepted completely before it can be read.
                The SOCK_RAW socket type provides single network access. This socket type uses the ICMP public protocol. (ping and traceroute use this protocol)
                SOCK_RDM This type is rarely used and is not implemented on most operating systems. It is provided to the data link layer and does not guarantee the order of data packets.
Protocol:

return value:

        Success: file descriptor

        Failure: -1

1.2 connect

#include <sys/types.h>

#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Function: connect to server

parameter:

        sockfd: socket file descriptor

        addr: address of the ipv4 socket structure, including IP address and port number

        addrlen: length of ipv4 socket structure

return value:

        Success: 0

        Failure: -1

1.3 bind

#include <sys/types.h>

#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Function: bind binding

parameter:

        sockfd: socket file descriptor

        addr: ipv4 socket structure, including IP address and port number

        addrlen: size of ipv4 socket structure

return value:

        Success: 0

        Failure: -1

1.4 listen

#include <sys/types.h>

#include <sys/socket.h>

int listen(int sockfd, int backlog);

Function: listen monitoring

parameter:

        sockfd: socket file descriptor

        backlog: The maximum value of the sum of the completed queue and the uncompleted queue is 128

                        View: cat /proc/sys/net/ipv4/tcp_max_syn_backlog 

return value:

        Success: 0

        Failure: -1

1.5 accept

#include <sys/types.h>

#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

Function: Extract new connections from the completed connection queue (if there are no new connections, accept will block)

parameter:

        sockfd: socket

        addr: ipv4 socket structure

        addrlen: address of the size of the ipv4 socket structure

return value:

        Success: File descriptor for new connected socket

        Failure: -1

1.6 Port reuse

Insert the following code between the socket and bind calls in the server code:

int opt = 1;

setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 

Note: Set a certain port in the program for reuse. Other network programs before this will not be able to use this port. 

2. Wrapping function

2.1 wrap.c

#include <stdlib.h>                                                                                                                                       
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>

void perr_exit(const char* s)
{
    perror(s);
    exit(-1);
}

int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr)
{
    int n;

again:
    if ((n = accept(fd, sa, salenptr)) < 0) {
        if ((errno == ECONNABORTED) || (errno == EINTR))//如果是被信号中断和软件层次中断,不能退出
            goto again;
        else
            perr_exit("accept error");
    }
    return n;
}

int Bind(int fd, const struct sockaddr* sa, socklen_t salen)
{
    int n;

    if ((n = bind(fd, sa, salen)) < 0)
        perr_exit("bind error");

    return n;
}
int Connect(int fd, const struct sockaddr* sa, socklen_t salen)
{
    int n;

    if ((n = connect(fd, sa, salen)) < 0)
        perr_exit("connect error");

    return n;
}

int Listen(int fd, int backlog)
{
    int n;

    if ((n = listen(fd, backlog)) < 0)
        perr_exit("listen error");

    return n;
}

int Socket(int family, int type, int protocol)
{
    int n;

    if ((n = socket(family, type, protocol)) < 0)
        perr_exit("socket error");

    return n;
}
ssize_t Read(int fd, void* ptr, size_t nbytes)
{
    ssize_t n;

again:
    if ((n = read(fd, ptr, nbytes)) == -1) {
        if (errno == EINTR)//如果是被信号中断,不应该退出
            goto again;
        else
            return -1;
    }
    return n;
}

ssize_t Write(int fd, const void* ptr, size_t nbytes)
{
    ssize_t n;

again:
    if ((n = write(fd, ptr, nbytes)) == -1) {
        if (errno == EINTR)
            goto again;
        else
            return -1;
    }
    return n;
}

int Close(int fd)
{
    int n;
    if ((n = close(fd)) == -1)
        perr_exit("close error");

    return n;
}

/*参三: 应该读取固定的字节数数据*/
ssize_t Readn(int fd, void* vptr, size_t n)
{
    size_t  nleft;              //usigned int 剩余未读取的字节数
    ssize_t nread;              //int 实际读到的字节数
    char* ptr;

    ptr = vptr;
    nleft = n;

    while (nleft > 0) {
        if ((nread = read(fd, ptr, nleft)) < 0) {
            if (errno == EINTR)
                nread = 0;
            else
                return -1;
        }
        else if (nread == 0)
            break;

        nleft -= nread;
        ptr += nread;
    }
    return n - nleft;
}

/*:固定的字节数数据*/
ssize_t Writen(int fd, const void* vptr, size_t n)
{
    size_t nleft;
    ssize_t nwritten;
    const char* ptr;

    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        if ((nwritten = write(fd, ptr, nleft)) <= 0) {
            if (nwritten < 0 && errno == EINTR)
                nwritten = 0;
            else
                return -1;
        }

        nleft -= nwritten;
        ptr += nwritten;
    }
    return n;
}

static ssize_t my_read(int fd, char* ptr)
{
    static int read_cnt;
    static char* read_ptr;
    static char read_buf[100];

    if (read_cnt <= 0) {
    again:
        if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
            if (errno == EINTR)
                goto again;
            return -1;
        }
        else if (read_cnt == 0)
            return 0;
        read_ptr = read_buf;
    }
    read_cnt--;
    *ptr = *read_ptr++;

    return 1;
}

ssize_t Readline(int fd, void* vptr, size_t maxlen)
{
    ssize_t n, rc;
    char    c, * ptr;

    ptr = vptr;
    for (n = 1; n < maxlen; n++) {
        if ((rc = my_read(fd, &c)) == 1) {
            *ptr++ = c;
            if (c == '\n')
                break;
        }
        else if (rc == 0) {
            *ptr = 0;
            return n - 1;
        }
        else
            return -1;
    }
    *ptr = 0;

    return n;
}

int tcp4bind(short port, const char* IP)
{
    struct sockaddr_in serv_addr;
    int lfd = Socket(AF_INET, SOCK_STREAM, 0);
    bzero(&serv_addr, sizeof(serv_addr));
    if (IP == NULL) {
        //如果这样使用 0.0.0.0,任意ip将可以连接
        serv_addr.sin_addr.s_addr = INADDR_ANY;
    }
    else {
        if (inet_pton(AF_INET, IP, &serv_addr.sin_addr.s_addr) <= 0) {
            perror(IP);//转换失败
            exit(1);
        }
    }
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    //端口复用
    int opt = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    Bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    return lfd;
}

2.2 wrap.h

#ifndef __WRAP_H_                                                                                                                                         
#define __WRAP_H_
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>

void perr_exit(const char* s);
int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr);
int Bind(int fd, const struct sockaddr* sa, socklen_t salen);
int Connect(int fd, const struct sockaddr* sa, socklen_t salen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void* ptr, size_t nbytes);
ssize_t Write(int fd, const void* ptr, size_t nbytes);
int Close(int fd);
ssize_t Readn(int fd, void* vptr, size_t n);
ssize_t Writen(int fd, const void* vptr, size_t n);
ssize_t my_read(int fd, char* ptr);
ssize_t Readline(int fd, void* vptr, size_t maxlen);
int tcp4bind(short port, const char* IP);
#endif

3.TCP server

Socket model creation flow chart:

3.1 Simple version

client.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define SERVER_IP "192.168.0.105"
#define SERVER_PORT 8008
int main()
{
    //创建套接字
    int sock_fd;
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    //连接服务器
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &addr.sin_addr);
    connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr));
    //读写数据
    char buf[1024] = "";
    while (1)
    {
        int n = read(STDIN_FILENO, buf, sizeof(buf));
        write(sock_fd, buf, n);//发送数据
        n = read(sock_fd, buf, sizeof(buf));
        write(STDOUT_FILENO, buf, n);
    }
    //关闭
    close(sock_fd);

    return 0;
}

server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>

#define SERVER_PORT 8008
#define SERVER_IP "192.168.0.106"
#define BACKLOG 128
int main()
{
    //创建套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    //绑定
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    //addr.sin_addr.s_addr = INADDR_ANY;  //绑定的是通配地址
    inet_pton(AF_INET, SERVER_IP, &addr.sin_addr.s_addr);
    bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
    //监听
    listen(lfd, BACKLOG);
    //提取
    struct sockaddr_in cliaddr;
    socklen_t len = sizeof(cliaddr);
    int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len);
    char ip[16] = "";
    printf("new client ip = %s port = %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, 16), ntohs(cliaddr.sin_port));
    //读写
    char buf[1024] = "";
    while (1)
    {
        bzero(buf, sizeof(buf));
        int n = read(STDIN_FILENO, buf, sizeof(buf));
        write(cfd, buf, n);
        n = read(cfd, buf, sizeof(buf));
        printf("%s\n", buf);
    }
    //关闭
    close(lfd);
    close(cfd);

    return 0;
}

After the client and server are started, you can use the netstat command to view the link status:

netstat -apn|grep 6666

3.2 Multi-process version

server.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include "wrap.h"

#define SERVER_PORT 8000
#define SERVER_IP "192.168.0.106" 
#define BACKLOG 128

void free_process(int sig)
{
    pid_t pid;
    while ((pid = waitpid(-1, NULL, WNOHANG)) > 0)
    {
        printf("child pid = %d has exited\n", pid);
    }
}
void handle_client(int cfd)
{
    char buf[1024];
    ssize_t n;

    while ((n = read(cfd, buf, sizeof(buf))) > 0)
    {
        printf("from clent :%s\n", buf);
        if (write(cfd, buf, n) < 0)
        {
            perror("Fail to sedn response to client");
            Close(cfd);
            exit(1);
        }
    }

    if (n < 0)
    {
        perror("Fail to read from client");
    }
    printf("Client closed connection\n");
    Close(cfd);
    exit(0);
}
int main()
{
    struct sigaction act;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    act.sa_handler = free_process;
    if (sigaction(SIGCHLD, &act, NULL) < 0)
    {
        perror("fail to sigaction");
        exit(1);
    }
    //创建套接字
    int lfd = tcp4bind(SERVER_PORT, NULL);
    //监听
    Listen(lfd, BACKLOG);
    //提前
    struct sockaddr_in cliaddr;
    socklen_t len = sizeof(cliaddr);

    while (1)
    {
        char ip[16] = "";
        //提取连接
        int cfd = Accept(lfd, (struct sockaddr*)&cliaddr, &len);
        printf("new client ip = %s port = %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, 16), ntohs(cliaddr.sin_port));
        //fork创建子进程
        pid_t pid;
        pid = fork();

        if (pid < 0)
        {
            perror("fail to fork");
            Close(cfd);
            continue;
        }
        else if (pid == 0)
        {
            Close(lfd);
            handle_client(cfd);
            break;
        }

        Close(cfd);
    }
    //关闭
    Close(lfd);

    return 0;
}

3.3 Multi-threaded version

server.c

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include "wrap.h"
#include <arpa/inet.h>

typedef struct c_info {
    int cfd;
    struct sockaddr_in cliaddr;
}CINFO;


void* client_fun(void* arg)
{
    CINFO* info = (CINFO*)arg;
    char ip[16] = "";
    printf("new client ip =%s port =%d\n", inet_ntop(AF_INET, &(info->cliaddr.sin_addr.s_addr), ip, 16), ntohs(info->cliaddr.sin_port));

    while (1)
    {
        char buf[1024] = "";
        int count = 0;
        count = read(info->cfd, buf, sizeof(buf));
        if (count < 0)
        {
            perror("");
            break;
        }
        else if (count == 0)
        {
            printf("client close\n");
            break;
        }
        else
        {
            printf("%s\n", buf);
            write(info->cfd, buf, count);
        }
    }
    Close(info->cfd);
    free(info);
    pthread_exit(NULL);
}
int main(int argc, char* argv[])
{
    if (argc < 2)
    {
        perr_exit("argc < 2\n ./a.out 8000\n");
    }
    pthread_attr_t attr;
    int s = pthread_attr_init(&attr);
    if (s != 0)
    {
        perr_exit("pthread_attr_init error");
    }
    s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (s != 0)
    {
        perr_exit("pthread_attr_setdetachstate error");
    }
    short port = atoi(argv[1]);
    int lfd = tcp4bind(port, NULL);

    Listen(lfd, 128);
    struct sockaddr_in cliaddr;
    socklen_t len = sizeof(cliaddr);
    CINFO* info;

    while (1)
    {
        int cfd = Accept(lfd, (struct sockaddr*)&cliaddr, &len);
        char ip[16] = "";

        pthread_t pthid;
        info = (CINFO*)malloc(sizeof(CINFO));
        if (NULL == info)
        {
            perr_exit("malloc error");
        }
        info->cfd = cfd;
        info->cliaddr = cliaddr;
        pthread_create(&pthid, &attr, client_fun, info);
    }
    return 0;
}

4. UDP server

        Compared with TCP, UDP communication is more like sending text messages. There is no need to establish and maintain connections before data transmission. Just focus on getting the data. By eliminating the three-way handshake process, the communication speed can be greatly improved, but the stability and accuracy of the accompanying communication cannot be guaranteed. Therefore, we call UDP "connectionless unreliable message delivery".

        Since there is no need to create a connection, UDP overhead is small, data transmission speed is fast, and real-time performance is strong. It is mostly used in communication situations with high real-time requirements, such as video conferencing, telephone conferencing, etc. However, it is also accompanied by unreliable data transmission, and the accuracy, transmission sequence and flow of transmitted data cannot be controlled and guaranteed. Therefore, under normal circumstances, the UDP protocol is used for data transmission. In order to ensure the correctness of the data, we need to add an auxiliary verification protocol at the application layer to make up for the shortcomings of UDP to achieve the purpose of reliable data transmission.

        Similar to TCP, UDP may also experience packet loss when receiving data after the buffer is filled. Since it does not have the TCP sliding window mechanism, the following two methods are usually used to solve it:

  1. The server application layer designs flow control to control the speed of sending data.
  2. Use the setsockopt function to change the receive buffer size. like:

#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t optlen);
int n = 220x1024
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));

C/S model UDP:

 

server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define SERVER_PORT 8001
#define MAXLINE 1024

int main()
{
    int sockfd;
    char buf[MAXLINE];
    struct sockaddr_in seraddr, cliaddr;

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("fail to socket");
        exit(1);
    }

    memset(&seraddr, 0, sizeof(seraddr));
    memset(&cliaddr, 0, sizeof(cliaddr));

    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SERVER_PORT);
    seraddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(sockfd, (const struct sockaddr*)&seraddr, sizeof(seraddr)) < 0)
    {
        perror("fail to bind");
        exit(1);
    }
    socklen_t len = sizeof(cliaddr);
    int n;

    while (1)
    {
        memset(buf, 0, sizeof(buf));
        n = recvfrom(sockfd, buf, MAXLINE, MSG_WAITALL, (struct sockaddr*)&cliaddr, &len);
        if (n < 0)
        {
            perror("fail to recvfrom");
            break;
        }
        else
        {
            printf("From Client data:%s\n", buf);
            if (sendto(sockfd, buf, n, 0, (const struct sockaddr*)&cliaddr, len) == -1)
            {
                perror("fail to sendto");
                break;
            }
        }
    }

    close(sockfd);
    return 0;
}

 client.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define SERVER_PORT 8001
#define MAXLINE 1024    

int main()
{
    int sockfd;
    char buf[MAXLINE];
    struct sockaddr_in seraddr;

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("fail to socket");
        exit(1);
    }

    memset(&seraddr, 0, sizeof(seraddr));

    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);

    int n;
    socklen_t len = sizeof(seraddr);

    while (1)
    {
        n = read(STDIN_FILENO, buf, sizeof(buf));
        if(sendto(sockfd, buf, n, 0, (const struct sockaddr*)&seraddr, len) == -1)
        {
            perror("fail to sendto");
            break;
        }

        memset(buf, 0, sizeof(buf));
        n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&seraddr, &len);
        if (n < 0)
        {
            perror("fail to recvfrom");
            break;
        }
        else
        {
            printf("From Server data:%s\n", buf);
        }
    }
    close(sockfd);

    return 0;
}

5. Local socket

        The socket API was originally designed for network communication, but later an IPC mechanism was developed based on the socket framework, which is UNIX Domain Socket. Although network sockets can also be used for inter-process communication on the same host (through loopback address 127.0.0.1), UNIX Domain Sockets are more efficient for IPC: there is no need to go through the network protocol stack, packaging and unpacking, and checksum calculation. , maintain sequence numbers and responses, etc., just copy application layer data from one process to another. This is because the IPC mechanism is essentially reliable communication, while network protocols are designed for unreliable communication. UNIX Domain Socket also provides two stream-oriented and packet-oriented API interfaces, similar to TCP and UDP, but message-oriented UNIX Domain Socket is also reliable, and messages will neither be lost nor out of sequence.

        UNIX Domain Socket is full-duplex and has rich API interface semantics. It has obvious advantages over other IPC mechanisms. It has become the most widely used IPC mechanism. For example, the communication between X Window server and GUI program is through UNIX Domain Socket. .

        The process of using UNIX Domain Socket is very similar to that of a network socket. You must first call socket() to create a socket file descriptor. The address family is specified as AF_UNIX, the type can be SOCK_DGRAM or SOCK_STREAM, and the protocol parameter is still specified as 0.

        The most obvious difference between UNIX Domain Socket and network socket programming is that the address format is different, represented by the structure sockaddr_un. The socket address of network programming is the IP address plus the port number, while the address of UNIX Domain Socket is a socket type file in the file system. The path of this socket file is created by the bind() call. If the file already exists when bind() is called, bind() returns an error.

        Compare the network socket address structure and the local socket address structure:

struct sockaddr_in {     __kernel_sa_family_t sin_family; /* Address family */ Address structure type         __be16 sin_port; /* Port number */ Port number         struct in_addr sin_addr; /* Internet address */ IP address }; struct sockaddr_un {     __kernel_sa_family_t sun_family; /* AF_UNIX * / Address structure type         char sun_path[UNIX_PATH_MAX]; /* pathname */ socket file name (including path) };







The following program binds a UNIX Domain socket to an address. 

size = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
#define offsetof(type, member) ((int)&((type *)0)->member) 

 service:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

char *socket_path = "/tmp/demo_socket";

int main(void) {
    struct sockaddr_un addr;
    char buf[100];
    int fd,cl,rc;

    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket error");
        exit(-1);
    }

    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);

    unlink(socket_path);

    if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        perror("bind error");
        exit(-1);
    }

    if (listen(fd, 5) == -1) {
        perror("listen error");
        exit(-1);
    }

    while (1) {
        if ((cl = accept(fd, NULL, NULL)) == -1) {
            perror("accept error");
            continue;
        }

        while ((rc=read(cl,buf,sizeof(buf))) > 0) {
            printf("read %u bytes: %.*s\n", rc, rc, buf);
            write(cl, buf, rc);
        }
        if (rc == -1) {
            perror("read");
            exit(-1);
        }
        else if (rc == 0) {
            printf("EOF\n");
            close(cl);
        }
    }

    return 0;
}

client:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

char *socket_path = "/tmp/demo_socket";

int main(void) {
    struct sockaddr_un addr;
    char buf[100];
    int fd,rc;

    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket error");
        exit(-1);
    }

    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);

    if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        perror("connect error");
        exit(-1);
    }

    while(1) {
        printf("Enter message to send: ");
        fgets(buf, sizeof(buf), stdin);
        if ((rc = write(fd, buf, strlen(buf))) > 0) {
            printf("Message sent\n");
            read(fd, buf, sizeof(buf));
            printf("Server replied : %s\n", buf);
        }
        else {
            printf("Error or connection closed\n");
            break;
        }
    }

    return 0;
}

Summarize:

        These are codes implemented in C language. It is recommended to understand them and type them out by yourself.

Guess you like

Origin blog.csdn.net/crr411422/article/details/131680574