Linux - C programming equipped with VS2022 for network programming to achieve client-server communication

Linux network programming

Programming flow based on stream sockets

flow chart:

Insert image description here

Process description:

Server: Establish a socket, declare its own port and IP, bind to the socket, use listen to monitor, and then continue to use accept to see if there is a connection. If there is, capture the socket and obtain the content of the message through recv. After the communication is completed, call closeSocket to close the socket corresponding to accept. If you do not need to wait for any client connection, use closeSocket to directly close your own socket.


Client: Establish a socket, determine the target server through the port number and address, use Connect to connect to the server, send the message, wait for processing, and call after the communication is completed. closeSocket closes the socket.

Before any type of communication can begin, the network application must create the socket.


A socket is originally created for an application on the same host, making it a program (aka a process) running on the host communicates with another running program. This is called Inter Process Communication (IPC)

There are two types of sockets:File-based and web-oriented

File-based
Family name: AF_UNIX
(also known as AF_LOCAL, specified in the POSIX1.g standard), which represents the address family (addressfamily ): UNIX. Other older systems may represent address families as domains or protocol families and use the abbreviation PF instead of AF. Similarly, AF_LOCAL (standardized in 2000-2001) will replace AF_UNIX

web-oriented

Family name: AF_INET
or Address family: Internet. Another address family, AF_INET6, is used for Internet Protocol version 6 (IPv6) addressing. Additionally, there are other address families, which are either specialized, obsolete, rarely used, or are still not implemented. Among all address families, AF_INET is currently the most widely used

Socket address: host-port pair:

socket, as the name suggests, means socket. It is used to describe the address and port. It is the handle of a communication chain. The application sends a request or response to the network through the socket.

As an analogy, a socket is like a telephone jack, and the host name and port number are like the area code and number. When programs need to communicate, they need to know the host name (IP) and port number of the other end. The valid port number range is 0~65535 (port numbers less than 1024 are reserved for the system)

There are three types of socket programming, stream socket (SOCK_STREAM), datagram socket (SOCK_DGRAM), and raw socket (SOCK_RAW). The first two are more commonly used. TCP-based socket programming is streaming sockets.

Connection-oriented sockets vs. connectionless sockets:

connection oriented socket

  • The name of the TCP socket SOCK_STREAM.
  • Features: Reliable, high cost.
    Prior to communication, a connection must be established that provides serialized, reliable, and non-duplicate data delivery< a i=3>, without recording boundaries. This type of communication is also called a virtual circuit or a stream socket. The main protocol that implements this connection type is the Transmission Control Protocol (abbreviated TCP) In order to create a TCP socket, Must useSOCK_STREAM as the socket type.

connectionless socket

  • UDP socket name SOCK_DGRAM
  • Features: Unreliable (it is relatively reliable within the local network), low overhead.
    In sharp contrast to the virtual circuit is the datagram type socket, which is a connectionless socket.
    There is no need to establish a connection before communication can begin. At this time, there is no guarantee of its sequence, reliability or repeatability during data transmission. Datagrams do preserve record boundaries, which means the message is sent as a whole rather than first broken into fragments.
    The main protocol that implements this type of connection is User Datagram Protocol (abbreviated UDP). To create a UDP socket for
    , you must use SOCK_DGRAM as the socket type .
    The SOCK_DGRAM name of the UDP socket comes from the word "datagram" (datagram).

Function description:

socket(AF_INET, SOCK_STREAM, 0)

The socket() function is similar to the open() function. It is used to create a network communication endpoint (open a network communication). If successful, it returns a network file descriptor< /span> (socket descriptor)socket descriptor, this file descriptor is usually called

Function definition: Create a socket of type type in the domain, using the protocol. If PROTOCOL is 0, a file descriptor that returns the new socket is automatically selected. , returning -1 indicates an error.

bind(server_socket, (struct sockaddr*) & server_addr, sizeof(server_addr))

bind() The function is used to bind an IP address or port number to a socket (bind the socket with the address Association), (note that the address mentioned here includes IP address and port number), because for the client to communicate with the server, it first needs to know the IP address of the server and the corresponding port number, So usually the server’s IP address and port number are well known

Function definition: Provides the local address ADDR (length is LEN bytes) for the socket FD

struct sockaddr_in server_addr

Structure definition: Describes the structure of an Internet socket address.

Generally we use struct sockaddr_in structure, sockaddr_in and sockaddr are side by side structure (the space occupied is the same), the pointer to the sockaddr_in structure can also point to the sockadd structure and replace it.

# 示例代码  struct sockaddr 结构体
struct sockaddr {
     
     
sa_family_t sa_family;
char sa_data[14];
}
// 第二个参数sa_data :是一个 char 类型数组,一共 14 个字节,在这 14 个字节中就包括了 IP 地址、端口号等信息

listen(server_socket, 10)

listen()The function can only be used in the server process.Let the server process enter the listening state, waiting for the client’s connection request. The listen() function is Generally called after the bind() function and before the accept() function

Function definition: Prepares to accept connections on socket FD. N connection requests will be queued before further requests are rejected. Returns 0 on success and -1 on error.

accept(server_socket, NULL, NULL)

After the server calls the listen() function, it will enter the listening state and wait for the client's connection request.Use the accept() function to obtain the client's connection. Request and establish connection.

Function definition: Wait for a connection on the socket FD (file descriptor). When a connection arrives, open a new socket to communicate with it, set ADDR (that is, ADDR_LEN byte length) to the address of the connected peer, ADDR_LEN to the actual length of the address, and return the new socket descriptor. -1 indicates an error.

read(accept_socket, buffer, sizeof(buffer))

Function definition: Read N_BYTES from FD to BUF. Returns the number read, -1 indicates error, 0 indicates EOF. This function is a cancellation point, so the THROW mark is not used.

write(accept_socket, buffer, res)

Write N bytes of BUF to FD. Returns the number written, or -1 on error. This function is a cancellation point, so the THROW mark is not used.

connect()

This function is used in client applications. The client calls the connect() function to connect the socket sockfd to the remote server.

#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
int main()
{
    
    
    char buffer[50] = {
    
    0};
    int res = 0;
    int server_socket; //这个是socket网络描述符,也叫套接字描述符。
    int accept_socket;
    // 第一步:创建套接字描述符       --“买一部手机”
    printf("开始创建tcp服务器\n");
    server_socket = socket(AF_INET, SOCK_STREAM, 0);//我们想要向网络发送数据都使用
                                                    //server_socket这个套接字描述符
    
    if (server_socket < 0)
    {
    
    
        perror("socke create failed:");
        return 0;
    }

    // 第二步:要告诉这个服务器 我的ip地址和端口号 我们要有一个保存ip地址和端口的变量
                //          --“买电话卡”
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET; // ipv4
    server_addr.sin_addr.s_addr = INADDR_ANY;   //inaddr_any 告诉系统自动绑定网卡ip地址
    server_addr.sin_port = htons(6668);    //网络地址转换,把主机字节顺序转换成网络字节顺序

    // 第三步:把我们设定好的ip地址和端口号绑定到我们的server_socket描述符上。 --“把电话卡插入手机上”

    if (bind(server_socket, (struct sockaddr*) & server_addr, sizeof(server_addr)) < 0)
    {
    
    
        perror("server bind error:");
        return 0;
    }

    // 第四步:我们调用listen 开始监听程序    --“把电话放在身上,电话铃响听到声音”
    if (listen(server_socket, 10) < 0)
    {
    
    
        perror("server listen error:");
        return 0;
    }

    // 第五步:以上4个步骤都ok后,我们就可以等待客户端连接过来了。
    
    // accept 函数有一个特点,当我们程序调用这个函数的时候,如果没有客户端连接到我们的服务器,
    // 那么这个函数将堵塞(程序停下不走了),直到有客户端连接到服务器,这个函数将解开,并
    // 并且返回一个新的套接字描述符。那么后期和客户端的通讯都交给这个新的套接字描述符来负责。
    printf("TCP服务器准备完成,等待客户端连接!\n");
    accept_socket = accept(server_socket, NULL, NULL);     //如果有客户端连接,返回一个新的套接字变量。
    printf("有客户端连接到服务器!\n");
    while (1)
    {
    
    
        //read函数就是接受客户端发来的数据,返回实际从客户端那边收到的字节数。
        //buffer: 收到客户端数据后把数据存放的地址  sizeof(buffer) 就是希望读取的字节数
        res = read(accept_socket, buffer, sizeof(buffer));
        printf("client read %s\n", buffer);
        write(accept_socket, buffer, res);
        memset(buffer, 0, sizeof(buffer));

    }
    printf("%s 向你问好!\n", "LINUX_C_SAMPLE");
    return 0;
}

Guess you like

Origin blog.csdn.net/m0_63669388/article/details/132707158