Linux network programming (c/s model)

Network byte order:

The concept of big endian and little endian:

Big end: the status address stores high-order data, and the high-order address stores status data

Little endian: the status address stores the status data, and the high address stores the high data

The storage method of data in the computer is in the form of little endian , but it is stored in the form of big endian in the network , so there are several functions in Linux to convert the storage method

The header file included by the function : <arpa/inet.h>

  1. htonl function: uint32_t htonl(uint32_t hostlong);

Function: Convert the local (little endian) IP address to the required IP address form in the network (big endian)

  1. htons function: uint16_t htons(uint16_t hostshort);

Function: Convert the local (little endian) port number to the port number required in the network (big endian)

  1. ntohl function: uint32_t ntohl(uint32_t netlong);

Function: Convert the (big-endian) IP address in the network to the local (little-endian) IP address form

  1. ntohs function: uint16_t ntohs(uint16_t netshort);

Function: Convert the (big endian) port number in the network to the local (little endian) port number form

Commands to view listening status and connection status

netstat command: netstat -anp | grep 8888

a means show all

n indicates that it is displayed in a digital way when displaying

p means to display the message of the process

  1. simple c/s model

simple c/s model

1. Function usage:

1.socket function: included in the header file <sys/types.h> or <sys/socket.h>

➡Function prototype: int socket(int domain, int type, int protocol);

Role: Create a socket for connection

2.connect function: included in the header file <sys/types.h> or <sys/socket.h>

➡Function prototype: int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Role: function to connect to the server

3.memset function: included in the header file <string.h>

➡Function prototype: void *memset(void *s, int c, size_t n);

Function: Set the content in the array to a certain value

4.inet_pton and inet_ntop functions: included in the header file <arpa/inet.h>

➡Function prototype: int inet_pton(int af, const char *src, void *dst);

Parameter af: the specified IP protocol type, there are two options AF_INET and AF_INET6

Parameter src: incoming IP address (dotted decimal)

Parameter dst: Outgoing address, converted network byte order IP address

Return value: 1 is returned on success, 0 is returned on exception (that is, it points to an invalid IP address), and -1 is returned on failure

Function: attach the address value to the value of s_addr in the sockaddr_in structure, and also convert the byte order of the dotted decimal system into the byte order of the network

➡Function prototype: const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);

Parameter af: the specified IP protocol type, there are two options AF_INET and AF_INET6

Parameter src: incoming IP address, network byte order IP address

Parameter dst: buffer stored after address translation, local byte order

Parameter size: buffer size

Return value: return dst if successful, return NULL if failed

Function: Convert network byte order to dotted decimal byte order

5.bind function: included in the header file <sys/types.h> or <sys/socket.h>

➡Function prototype: int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Role: bind IP address

6.listen function: included in the header file <sys/types.h> or <sys/socket.h>

➡Function prototype: int listen(int sockfd, int backlog);

Role: Listen to client link requests

7. accept function: included in the header file <sys/types.h> or <sys/socket.h>

➡Function prototype: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

Role: accept the connection from the client

server code

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>//使用网络包含的库
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>

int main()
{
        int lfd = socket(AF_INET, SOCK_STREAM, 0);
        if(lfd < 0)
        {
                perror("soket error");
                return -1;
        }
        struct sockaddr_in serv;
        bzero(&serv, sizeof(serv));//该函数初始化serv
        serv.sin_family = AF_INET;
        serv.sin_port = htons(8888);
        serv.sin_addr.s_addr = htonl(INADDR_ANY);//表示使用本地任意可用IP地址
        int ret = bind(lfd, (struct sockaddr*)&serv, sizeof(serv));
        if(ret < 0)
        {
                perror("bind error");
                return -1;
        }
        //监听
        listen(lfd, 128);
        //接受链接
        int cfd = accept(lfd, NULL, NULL);
        printf("lfd==[%d], cfd==[%d]\n", lfd, cfd);

        int n = 0;
        int i = 0;
        char buf[1024];
        while(1)
        {
                //读数据
                memset(buf, 0x00, sizeof(buf));
                n = read(cfd, buf, sizeof(buf));
                if(n <= 0)
                {
                        printf("read error or client close\n");
                        break;
                }
                printf("n==[%d], buf==[%s]\n", n, buf);

                for(i = 0;i < n;i++)
                {
                        //buf[i] = toupper(buf[i]);//该函数把小写字母转换成大写字母
                }
                //发生数据
                write(cfd, buf, n);
        }
        //关闭监听文件描述符和通信文件描述符
        close(lfd);
        close(cfd);
        return 0;
}

                                   

client code

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

int main()
{
        //创建socket,与服务端进行通信
        int cfd = socket(AF_INET, SOCK_STREAM, 0);
        if(cfd < 0)
        {
                perror("socket error");
                return -1;
        }
        //连接服务器
        struct sockaddr_in serv;
        serv.sin_family = AF_INET;
        serv.sin_port = htons(8888);
        inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);//赋值地址
        printf("[%x]\n", serv.sin_addr.s_addr);//输出地址
        int ret = connect(cfd, (struct sockaddr*)&serv, sizeof(serv));
        if(ret < 0)
        {
                perror("connect error");
                return -1;
        }
        int n = 0;
        char buf[256];
        while(1)
        {
                //读标准输入数据
                memset(buf, 0x00, sizeof(buf));
                n = read(STDIN_FILENO, buf, sizeof(buf));
                //发送数据
                write(cfd, buf, n);
                //读服务端发来的数据
                memset(buf, 0x00, sizeof(buf));
                n = read(cfd, buf, sizeof(buf));
                if(n <= 0)
                {
                        printf("read error or server closed\n");
                        break;
                }
                printf("n==[%d], buf==[%s]\n", n, buf);
        }
        //关闭套接字
        close(cfd);
        return 0;
}
  1. Multi-process concurrent c/s model

1. Functions used

  1. fork function: included in the header files <sys/types.h> and <unistd.h>

➡Function prototype: pid_t fork(void);

Role: create a child process

Return value: the fork function has two return values

In the parent process, fork will return the ID of the newly created child process

In the child process, fork will return 0

Note: After calling the fork function, all code behind the fork function will be executed twice

Two execution processes:

If the fork is executed successfully, it will first return a number greater than 0, which is the ID of the child process, and execute the code of the parent process. After executing the code behind the fork function, the program will execute the code behind the fork function again. When fork returns a value of 0

  1. struct sigaction structure: included in the header file <signal.h>

Structure prototype:

struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

Member sa_handler: This is a function pointer, specifying a signal processing function

Member sa_mask: used to temporarily set aside the signal set specified by sa_mask when processing the signal

Member sa_falgs: used to set other related operations of signal processing, the following values ​​are available

Available options:

1. SA_RESETHAND : When the signal processing function is called, reset the signal processing function to the default value SIG_DFL

2. SA_RESTART : If the signal interrupts a system call of the process, the system will automatically start the system call

3. SA_NODEFER : Generally, when the signal processing function is running, the kernel will block the given signal. But if the SA_NODEFER flag is set, the kernel will not block the signal while the signal handler is running

  1. sigaction function: included in the header file <signal.h>

➡Function prototype: int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

Role: Check or modify the processing action associated with the specified signal

The signum parameter indicates the type of signal to capture

The act parameter specifies the new signal handling method,

The oldact parameter outputs the processing method of the previous signal

server code

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

#include "wrap.h"

#define SRV_PORT 10110

void catch_child(int signum)
{
    while((waitpid(0, NULL, WNOHANG))>0);//阻塞子进程
    return;
}

int main(int argc, int *argv[])
{
    int lfd;
    struct sockaddr_in srv_addr;
    //memset(&srv_addr, 0, sizeof(srv_addr));
    bzero(&srv_addr, sizeof(srv_addr));
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_port = htons(SRV_PORT);
    srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    lfd = Socket(AF_INET, SOCK_STREAM, 0);

    Bind(lfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
    
    Listen(lfd, 128);

    struct sockaddr_in clt_addr;//客户端使用的
    socklen_t clt_addr_len = sizeof(clt_addr);//accept参数需要使用的类型
    pid_t pid;
    int cfd;
    int retf;
    while(1)
    {
        cfd = Accept(lfd, (struct sockaddr*)&clt_addr, &clt_addr_len);
        //创建子进程
        pid = fork();
        if(pid < 0)//子进程创建失败
        {
            perr_exit("fork error");
        }
        else if(pid == 0){//子进程创建成功
            break;
        }
        else//处理主进程
       {
            //回收子进程
            struct sigaction act;//创建一个结构体
            act.sa_handler = catch_child;//捕捉子进程           
            sigemptyset(&act.sa_mask);//清空子进程
            act.sa_flags = 0;
            retf = sigaction(SIGCHLD, &act, NULL);
            if(retf != 0)
            {
                perr_exit("sigaction error");
            }

            close(cfd);
            continue;
        }
    }
    char buf[1024] = {0};
    int ret = 0;
    int i = 0;
    if(pid == 0)
    {
        while(1)
        {
            ret = Read(cfd,buf, sizeof(buf));
            if(ret == 0)
            {
                close(cfd);
                exit(0);
            }
            for(i = 0;i < ret; i++);
            {
                buf[i] = toupper(buf[i]);
            }
            write(cfd, buf, ret);
            write(STDOUT_FILENO, buf, ret);
        }
    } 
    return 0;
}

3. Multithreaded concurrent c/s model

1. Functions used

1.pthread_create function: included in the header file <pthread.h>

➡函数原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

Function: create a child thread

Parameter 1: The parameter of type pthread_t created in advance. On success, the memory unit pointed to by tidp is set to the thread ID of the newly created thread

Parameter 2: Used to customize various thread attributes

Parameter 3: The newly created thread starts running from this function

Parameter 4: The parameter of the start_rtn function. If there is no parameter, set it to NULL. If there is a parameter, enter the address of the parameter. When more than one parameter should be passed by structure

Return value: If the thread is successfully created, return 0, otherwise return the error number

2.pthread_detach function: included in the header file <pthread.h>

➡Function prototype: int pthread_detach(pthread_t thread);

Function: In the thread separation state, the thread actively disconnects from the main control thread. After using pthread_exit or the thread automatically ends, its exit status is not obtained by other threads, but is automatically released directly

Parameters: pass in the specified thread number

Return value: 0 is returned on success, and an error number is returned on failure

server code

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>

#include "wrap.h"

#define MAXLINE 8192
#define SERV_PORT 8000

struct s_info {                     //定义一个结构体, 将地址结构跟cfd捆绑
    struct sockaddr_in cliaddr;
    int connfd;
};

void *do_work(void *arg)
{
    int n,i;
    struct s_info *ts = (struct s_info*)arg;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];      //#define INET_ADDRSTRLEN 16  可用"[+d"查看

    while (1) {
        n = Read(ts->connfd, buf, MAXLINE);                     //读客户端
        if (n == 0) {
            printf("the client %d closed...\n", ts->connfd);
            break;                                              //跳出循环,关闭cfd
        }
        printf("received from %s at PORT %d\n",
                inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),
                ntohs((*ts).cliaddr.sin_port));                 //打印客户端信息(IP/PORT)

        for (i = 0; i < n; i++) 
            buf[i] = toupper(buf[i]);                           //小写-->大写

        Write(STDOUT_FILENO, buf, n);                           //写出至屏幕
        Write(ts->connfd, buf, n);                              //回写给客户端
    }
    Close(ts->connfd);

    return (void *)0;//结束该子线程
}

int main(void)
{
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, connfd;
    pthread_t tid;
    struct s_info ts[256];      //根据最大线程数创建结构体数组.
    int i = 0;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);                     //创建一个socket, 得到lfd

    bzero(&servaddr, sizeof(servaddr));                             //地址结构清零
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);                   //指定本地任意IP
    servaddr.sin_port = htons(SERV_PORT);                           //指定端口号 8000

    Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); //绑定

    Listen(listenfd, 128);      //设置同一时刻链接服务器上限数

    printf("Accepting client connect ...\n");

    while (1) {
        cliaddr_len = sizeof(cliaddr);
        connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);   //阻塞监听客户端链接请求
        ts[i].cliaddr = cliaddr;
        ts[i].connfd = connfd;

        /* 达到线程最大数时,pthread_create出错处理, 增加服务器稳定性 */
        pthread_create(&tid, NULL, do_work, (void*)&ts[i]);
        pthread_detach(tid); //线程已经创建好,需要关闭线程句柄                                                //子线程分离,防止僵线程产生.
        i++;
    }

    return 0;
}

1.setsockopt function: included in the header files <sys/types.h> and <sys/socket.h>

Function prototype:

int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);

Role: Used to set option values ​​of any type and any state socket

Parameter meaning:

sockfd: a descriptor identifying a socket;

level: the level of option definition; support SOL_SOCKET, IPPROTO_TCP, IPPROTO_IP and IPPROTO_IPV6; optname: option to be set;

optval: pointer to the buffer that stores the new value of the option to be set;

optlen: optval buffer length;

Return value: Returns 0 if successful, returns -1 if an error occurs

use case

int opt = 1;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(opt));

2.getsockopt function: included in the header files <sys/types.h> and <sys/socket.h>

Function prototype:

int getsockopt(int sockfd, int level, int optname,
                      void *optval, socklen_t *optlen);

Function: used to obtain the current value of the option of any type and any state socket, and store the result in optval

Parameter meaning:

sockfd: a descriptor identifying a socket;

level: the level of option definition; support SOL_SOCKET, IPPROTO_TCP;

optname: the socket option to be obtained;

optval: pointer, pointing to the buffer where the obtained option value is stored;

optlen: pointer, pointing to the length value of the optval buffer;

Return value: Returns 0 if successful, returns -1 if an error occurs

4. Half closed

Meaning: In the TCP connection, A sends a FIN request to close, and after B responds with ACK, A enters the FIN_WAIT_2 state and enters a half-closed state (actually, only one end is closed)

The difference between the close function and the shutdown function:

The close function can only close a socket or a file descriptor

The shutdown function can only close reading, only close writing, close reading and writing, and the situation is more refined. At the same time, when multiple file descriptors point to the same socket, call shutdown to disconnect all the pointers

Notice:

  1. If there are multiple processes sharing a socket, every time close is called, the count will be decremented by 1 until the count reaches 0, that is, all the processes in use have called close, and the socket will be released.

  1. In multi-process, if a process calls shutdown(sfd, SHUT RDWR), other processes will not be able to communicate. However, if one or two processes close(sfd) will not affect other processes

shutdown function prototype : included in the header file <sys/socket.h>

int shutdown(int sockfd, int how);

Parameter sockfd: the specified socket

Parameter how: the specified disconnection method, with the following options

  • SHUT_RD: Turn off the read function on sockfd. This option will not allow sockfd to perform read operations. The socket no longer accepts data, and any data in the current socket buffer will be discarded.

  • SHUT_WR: Turn off the write function on sockfd, this option will not allow sockfd to write, the process cannot write to this socket

  • SHUT_RDWR: Turn off the read and write function of sockfd, which is equivalent to calling shutdown twice, first SHUT_RD, then SHUT_WR

Guess you like

Origin blog.csdn.net/weixin_62859191/article/details/128822145