Programación de red Linux (modelo c/s)

Orden de bytes de la red:

El concepto de big endian y little endian:

Big end: la dirección de estado almacena datos de orden superior y la dirección de orden superior almacena datos de estado

Little endian: la dirección de estado almacena los datos de estado y la dirección alta almacena los datos altos

El método de almacenamiento de datos en la computadora es en forma de little endian , pero se almacena en forma de big endian en la red , por lo que hay varias funciones en Linux para convertir el método de almacenamiento

El archivo de encabezado incluido por la función : <arpa/inet.h>

  1. función htonl: uint32_t htonl(uint32_t hostlong);

Función: convertir la dirección IP local (little endian) a la forma de dirección IP requerida en la red (big endian)

  1. función htons: uint16_t htons(uint16_t hostshort);

Función: convertir el número de puerto local (little endian) al número de puerto requerido en la red (big endian)

  1. función ntohl: uint32_t ntohl(uint32_t netlong);

Función: convertir la dirección IP (big-endian) en la red a la forma de dirección IP local (little-endian)

  1. función ntohs: uint16_t ntohs(uint16_t netshort);

Función: convertir el número de puerto (big endian) en la red a la forma de número de puerto local (little endian)

Comandos para ver el estado de escucha y el estado de conexión

comando netstat: netstat -anp | grep 8888

un medio mostrar todo ocultar todo

n indica que se visualiza de forma digital al visualizar

p significa mostrar el mensaje del proceso

  1. modelo simple c/s

modelo simple c/s

1. Uso de la función:

1. Función de socket: incluida en el archivo de encabezado <sys/types.h> o <sys/socket.h>

➡Prototipo de función: socket int(dominio int, tipo int, protocolo int);

Rol: crear un socket para la conexión

2. Función de conexión: incluida en el archivo de encabezado <sys/types.h> o <sys/socket.h>

➡Prototipo de función: int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Rol: función para conectarse al servidor

Función 3.memset: incluida en el archivo de encabezado <string.h>

➡Prototipo de función: void *memset(void *s, int c, size_t n);

Función: establece el contenido de la matriz en un valor determinado

4. Funciones inet_pton e inet_ntop: incluidas en el archivo de cabecera <arpa/inet.h>

➡Prototipo de función: int inet_pton(int af, const char *src, void *dst);

Parámetro af: el tipo de protocolo IP especificado, hay dos opciones AF_INET y AF_INET6

Parámetro src: dirección IP entrante (decimal punteado)

Parámetro dst: dirección saliente, dirección IP de orden de bytes de red convertida

Valor devuelto: 1 se devuelve en caso de éxito, 0 se devuelve en caso de excepción (es decir, apunta a una dirección IP no válida) y -1 se devuelve en caso de error

Función: adjunte el valor de la dirección al valor de s_addr en la estructura sockaddr_in, y también convierta el orden de bytes del sistema decimal punteado en el orden de bytes de la red

➡Prototipo de función: const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);

Parámetro af: el tipo de protocolo IP especificado, hay dos opciones AF_INET y AF_INET6

Parámetro src: dirección IP entrante, dirección IP de orden de bytes de red

Parámetro dst: búfer almacenado después de la traducción de direcciones, orden de bytes local

Tamaño del parámetro: tamaño del búfer

Valor devuelto: devuelve dst si tiene éxito, devuelve NULL si falla

Función: convertir el orden de bytes de la red en orden de bytes decimales con puntos

5. función de vinculación: incluida en el archivo de encabezado <sys/types.h> o <sys/socket.h>

➡Prototipo de función: int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Rol: enlazar dirección IP

6. Función de escucha: incluida en el archivo de encabezado <sys/types.h> o <sys/socket.h>

➡Prototipo de función: int listen(int sockfd, int backlog);

Rol: escuchar las solicitudes de enlace del cliente

7. Función de aceptación: incluida en el archivo de encabezado <sys/types.h> o <sys/socket.h>

➡Prototipo de función: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

Rol: aceptar la conexión del cliente

código del servidor

#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;
}

                                   

codigo del cliente

#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. Modelo c/s concurrente multiproceso

1. Funciones utilizadas

  1. función de bifurcación: incluida en los archivos de encabezado <sys/types.h> y <unistd.h>

➡Prototipo de función: pid_t fork(void);

Rol: crear un proceso hijo

Valor de retorno: la función fork tiene dos valores de retorno

En el proceso principal, la bifurcación devolverá la ID del proceso secundario recién creado

En el proceso hijo, la bifurcación devolverá 0

Nota: Después de llamar a la función de bifurcación, todo el código detrás de la función de bifurcación se ejecutará dos veces

Dos procesos de ejecución:

Si la bifurcación se ejecuta con éxito, primero devolverá un número mayor que 0, que es el ID del proceso secundario, y ejecutará el código del proceso principal.Después de ejecutar el código detrás de la función de bifurcación, el programa ejecutará el código detrás de la función fork de nuevo. Cuando fork devuelve un valor de 0

  1. struct sigaction estructura: incluido en el archivo de cabecera <signal.h>

Prototipo de estructura:

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

Miembro sa_handler: este es un puntero de función que especifica una función de procesamiento de señal

Miembro sa_mask: se utiliza para dejar de lado temporalmente el conjunto de señales especificado por sa_mask al procesar la señal

Miembro sa_falgs: se utiliza para establecer otras operaciones relacionadas con el procesamiento de señales, los siguientes valores están disponibles

Opciones Disponibles:

1. SA_RESETHAND : cuando se llama a la función de procesamiento de señales, restablece la función de procesamiento de señales al valor predeterminado SIG_DFL

2. SA_RESTART : si la señal interrumpe una llamada al sistema del proceso, el sistema iniciará automáticamente la llamada al sistema

3. SA_NODEFER : Generalmente, cuando la función de procesamiento de señales se está ejecutando, el kernel bloqueará la señal dada. Pero si se establece el indicador SA_NODEFER, el kernel no bloqueará la señal mientras se ejecuta el controlador de señal.

  1. función sigaction: incluida en el archivo de cabecera <signal.h>

➡Prototipo de función: int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

Rol: Verifique o modifique la acción de procesamiento asociada con la señal especificada

El parámetro signum indica el tipo de señal a capturar

El parámetro act especifica el nuevo método de manejo de señales,

El parámetro oldact genera el método de procesamiento de la señal anterior

código del servidor

#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. Modelo c/s concurrente de subprocesos múltiples

1. Funciones utilizadas

1. Función pthread_create: incluida en el archivo de cabecera <pthread.h>

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

Función: crear un subproceso secundario

Parámetro 1: el parámetro de tipo pthread_t creado con anterioridad. En caso de éxito, la unidad de memoria a la que apunta tidp se establece en el ID de subproceso del subproceso recién creado

Parámetro 2: se utiliza para personalizar varias propiedades de subprocesos

Parámetro 3: el subproceso recién creado comienza a ejecutarse desde esta función

Parámetro 4: el parámetro de la función start_rtn. Si no hay ningún parámetro, configúrelo en NULL. Si hay un parámetro, ingrese la dirección del parámetro. Cuando se debe pasar más de un parámetro por estructura

Valor de retorno: si el hilo se crea correctamente, devuelve 0; de lo contrario, devuelve el número de error

2. Función pthread_detach: incluida en el archivo de encabezado <pthread.h>

➡Prototipo de función: int pthread_detach(pthread_t thread);

Función: en el estado de separación de subprocesos, el subproceso se desconecta activamente del subproceso de control principal. Después de usar pthread_exit o el subproceso finaliza automáticamente, otros subprocesos no obtienen su estado de salida, sino que se libera automáticamente directamente

Parámetros: pasar el número de hilo especificado

Valor de retorno: se devuelve 0 en caso de éxito y se devuelve un número de error en caso de falla

código del servidor

#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. Función setsockopt: incluida en los archivos de cabecera <sys/types.h> y <sys/socket.h>

Prototipo de función:

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

Rol: se utiliza para establecer valores de opción de cualquier tipo y cualquier estado de socket

Significado del parámetro:

sockfd: un descriptor que identifica un socket;

nivel: el nivel de definición de la opción; soporte SOL_SOCKET, IPPROTO_TCP, IPPROTO_IP e IPPROTO_IPV6; optname: opción a configurar;

optval: puntero al búfer que almacena el nuevo valor de la opción a configurar;

optlen: longitud del búfer optval;

Valor devuelto: Devuelve 0 si tiene éxito, devuelve -1 si se produce un error

caso de uso

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

2. Función getsockopt: incluida en los archivos de cabecera <sys/types.h> y <sys/socket.h>

Prototipo de función:

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

Función: se utiliza para obtener el valor actual de la opción de cualquier tipo y estado socket, y almacenar el resultado en optval

Significado del parámetro:

sockfd: un descriptor que identifica un socket;

nivel: el nivel de definición de opciones, soporte SOL_SOCKET, IPPROTO_TCP;

optname: la opción de socket a obtener;

optval: puntero, que apunta al búfer donde se almacena el valor de la opción obtenida;

optlen: puntero, que apunta al valor de longitud del búfer optval;

Valor devuelto: Devuelve 0 si tiene éxito, devuelve -1 si se produce un error

4. Medio cerrado

Significado: en la conexión TCP, A envía una solicitud FIN para cerrar, y después de que B responde con ACK, A ingresa al estado FIN_WAIT_2 y entra en un estado medio cerrado (en realidad, solo un extremo está cerrado)

La diferencia entre la función de cierre y la función de apagado:

La función de cierre solo puede cerrar un socket o un descriptor de archivo

La función de apagado solo puede cerrar la lectura, solo cerrar la escritura, cerrar la lectura y la escritura, y la situación es más refinada. Al mismo tiempo, cuando varios descriptores de archivo apunten al mismo socket, llame al apagado para desconectar todos los punteros.

Aviso:

  1. Si hay varios procesos compartiendo un socket, cada vez que se llama a close, la cuenta se decrementará en 1 hasta que la cuenta llegue a 0, es decir, todos los procesos en uso han llamado a close y el socket se liberará.

  1. En multiproceso, si un proceso llama a shutdown(sfd, SHUT RDWR), otros procesos no podrán comunicarse. Sin embargo, si uno o dos procesos se cierran (sfd) no afectará a otros procesos

prototipo de función de apagado : incluido en el archivo de encabezado <sys/socket.h>

int shutdown(int sockfd, int how);

Parámetro sockfd: el socket especificado

Parámetro cómo: el método de desconexión especificado, con las siguientes opciones

  • SHUT_RD: Desactive la función de lectura en sockfd. Esta opción no permitirá que sockfd realice operaciones de lectura. El socket ya no acepta datos, y cualquier dato en el búfer del socket actual será descartado.

  • SHUT_WR: apague la función de escritura en sockfd, esta opción no permitirá que sockfd escriba, el proceso no puede escribir en este socket

  • SHUT_RDWR: desactive la función de lectura y escritura de sockfd, que es equivalente a llamar a shutdown dos veces, primero SHUT_RD, luego SHUT_WR

Supongo que te gusta

Origin blog.csdn.net/weixin_62859191/article/details/128822145
Recomendado
Clasificación