4. Network programming - TCP-related API and its implementation steps

Table of contents

1. APIs

1. listen (connection socket)

2. accept (waiting for connection)

Notice

3. send (send data to TCP socket)

4. recv (receive data from TCP socket)

5. connect (connect to the peer listening socket)

2. Use steps

1. Server

2. Client

3. Code

1. Server (receiving end)

2. Client (sender)

1. APIs

1. listen (connection socket)

Set the connected socket as the listening socket, and set the maximum number of connection requests received at the same time

int listen(int sockfd, int backlog);
参数:
    sockfd:待连接套接字
    backlog:最大同时接收连接请求个数
返回值:
    成功:0,并将 sockfd 设置为监听套接字
    失败:-1

Due to historical reasons, various systems have different understandings of backlog. Taking LINUX as an example, the maximum number of connection requests that the listener can receive at the same time is backlog+4

2. accept (waiting for connection)

​
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
    sockfd:监听套接字
    addr:通用地址结构体,用以存储对端地址(IP+PORT)
    addrlen:参数 addr 的存储区域大小
返回值:
    成功:已连接套接字(非负整数)
    失败:-1

​

Notice

        This function is used to wait for the client to connect. If a new client connects, the function will return a new sockfd as the connection socket of the client.

        If accept is written inside the loop body, this function will cause blocking, and if there are multiple clients connected, this function will return multiple connected sockets corresponding to these clients.

3. send (send data to TCP socket)

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
    sockfd:已连接套接字
    buf:即将被发送的数据
    len:数据长度
    flags:发送标志。
        MSG_NOSIGNAL:当对端已关闭时,不产生 SIGPIPE 信号
        MSG_OOB:发送紧急(带外)数据,只针对 TCP 连接
返回值:
    成功:已发送字节数
    失败:-1

When flags is 0, send has the same effect as write.

4. recv (receive data from TCP socket)

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数:
    sockfd:已连接套接字
    buf:存储数据缓冲区
    len:缓冲区大小
    flags:接收标志
        MSG_OOB:接收紧急(带外)数据
返回值:
    成功:已接收字节数
    失败:-1

Note: when flags is 0, recv has the same effect as read.

5. connect (connect to the peer listening socket)

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:    
    sockfd:待连接套接字
    addr:包含对端地址(IP+PORT)的通用地址结构体的指针
    addrlen:地址结构体大小
返回值:
    成功:0
    失败:-1

2. Use steps

1. Server

(1) Create a tool for communication (socket)

(2) Set IP+port number

(3) Binding port number and other information (bind)

(4) Set up monitoring (listen)

(5) Waiting for the client to connect (accept)

(6) Chat with each other (send, recv)

(7) Close the chat (socket)

2. Client

(1) Create a tool for communication (socket)

(2) Set the IP address and port number of the service area to be connected

(3) Try to connect to the server (connect)

(4) Mutual chat (send, recv)

(5) Close the chat (socket)

3. Code

1. Server (receiving end)

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>  // 包含了地址结构体的定义声明
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>


int main(int argc, char const *argv[])
{
    // 创建一个套接字 (购买一台手机)
    //IPV4、流式套接字
    int sock_fd = socket( AF_INET , SOCK_STREAM , 0 ); // 使用IPV4协议簇, 流式套接字(TCP)
    if (-1 == sock_fd)
    {
        perror("socket rror");
        return -1 ;
    }
    

    // 配置自己的IP和端口号等信息
    // struct sockaddr_in   // IPV4地址结构体
    // {
    //     u_short sin_family;// 地址族
    //     u_short sin_port;// 端口
    //     struct in_addr sin_addr;// IPV4 地址
    //     char sin_zero[8];
    // };
    int addrlen = sizeof(struct sockaddr_in);
    struct sockaddr_in my_addr = {
        .sin_addr.s_addr = htonl(INADDR_ANY) , // 设置服务器的地址, INADDR_ANY 指本机中任何一个地址
        .sin_family = AF_INET , // 使用 IPV4 协议簇
        .sin_port = htons(65000) // 设置服务器的端口号为  65000 
    };

    // 绑定地址信息 (IP + 端口号 + 协议簇)
    int ret_val =  bind(  sock_fd,  (struct sockaddr *)&my_addr, addrlen);
    if (ret_val == -1 )
    {
        perror("bind error");
        return -1 ;
    }else{
        printf(" bind succeed !!\n") ;
    }
    
    // 设置监听 (打开铃声)
    if(listen( sock_fd,  5 )) // 设置sock_fd 为监听套接字, 同时可以接收连接请求数为  5 + 4(默认值) 
    {
        perror("listen error");
        return -1 ;
    }

    // 等待连接
    struct sockaddr_in from_addr; 
    int connect_fd = accept( sock_fd , (struct sockaddr *)&from_addr, &addrlen);
    if (connect_fd == -1 )
    {
        perror("connect error");
        return -1 ;
    }
    else{
        printf("connect succeed !!\n");
    }
    
    // 聊天
    char * msg = calloc(128,1);
    while(1)
    {
        bzero(msg , 128 );
        ret_val =  recv(connect_fd , msg , 128 , 0 );
        if ( ret_val != -1 )
        {
            printf("recv msg : %s " , msg );
        }
        else{
            perror("recv error !!");
        }
    }
    // 关闭套接字
    close(sock_fd);
   
    return 0;
}

2. Client (sender)

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>  // 包含了地址结构体的定义声明
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>


int main(int argc, char const *argv[])
{
    // 创建一个套接字 (购买一台手机)
    int sock_fd = socket( AF_INET , SOCK_STREAM , 0 ); // 使用IPV4协议簇, 流式套接字(TCP)
    if (-1 == sock_fd)
    {
        perror("socket rror");
        return -1 ;
    }
    

    // 配置自己的IP和端口号等信息
    // struct sockaddr_in   // IPV4地址结构体
    // {
    //     u_short sin_family;// 地址族
    //     u_short sin_port;// 端口
    //     struct in_addr sin_addr;// IPV4 地址
    //     char sin_zero[8];
    // };
    int addrlen = sizeof(struct sockaddr_in);
    struct sockaddr_in server_addr = {
        .sin_addr.s_addr = inet_addr("192.168.102.2") , // 设置服务器的地址
        .sin_family = AF_INET , // 使用 IPV4 协议簇
        .sin_port = htons(65000) // 设置服务器的端口号为  65000 
    };

  
    // 连接服务器
    if( connect(sock_fd , (const struct sockaddr *)&server_addr, addrlen))
    {
        perror("connect error");
        return -1 ;
    }

    
    // 聊天
    char * msg = calloc(128,1);
    while(1)
    {
        bzero(msg , 128 );
        fgets(msg , 128 , stdin);
        int ret_val =  send(sock_fd , msg , strlen(msg) , 0 );
        if ( ret_val != -1 )
        {
            printf("send msg succeed : %d byte \n" , ret_val );
        }
        else{
            perror("send error !!");
        }
    }

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

    return 0;
}

Disadvantages of the above procedures

If the client suddenly goes offline, the server crashes (requires a three-way handshake)

Guess you like

Origin blog.csdn.net/weixin_45981798/article/details/129904091