I/O函数

106 阅读3分钟

send函数

ssize_t send(int sockfd, const void * buf, size_t nbytes, int flags)
成功时返回发送的字节数,失败返回-1

sockfd:套接字文件描述符
buf:保存待传输数据的缓冲地址值
nbytes:待传输的字节数
flags:可选项

recv函数

ssize_t recv(int sockfd, const void * buf, size_t nbytes, int flags)
成功时返回发送的字节数,失败返回-1

sockfd:套接字文件描述符
buf:保存接收数据的缓冲地址值
nbytes:可接收的最大字节数
flags:可选项

image.png

MSG_OOB:发送紧急信息

MSG_OOB可选项用于创建特殊发送方法和通道用来发送紧急信息。

发送紧急信息

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

#define BUF_SIZE 30
void error_handling(char *message);
  
int main(int argc, char *argv[]) {
    int sock;
    struct sockaddr_in recv_adr;
    if (argc != 3) {
        printf("usage: %s <IP> <port> \n", argv[0]);
        exit(1);
    }

    sock = socket(AF_INET, SOCK_STREAM, 0);
    memset(&recv_adr, 0, sizeof(recv_adr));
    recv_adr.sin_family = AF_INET;
    recv_adr.sin_addr.s_addr = inet_addr(argv[1]);
    recv_adr.sin_port = htons(atoi(argv[2]));
    if (connect(sock, (struct sockaddr *)&recv_adr, sizeof(recv_adr)) == -1) {
        error_handling("connect() error!!!");
    }
    write(sock, "123", strlen("123"));
    send(sock, "4", strlen("4"), MSG_OOB);
    write(sock, "567", strlen("567"));
    send(sock, "890", strlen("890"), MSG_OOB);
    close(sock);
    return 0;
}

void error_handling(char *message) {
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

接收紧急信息

#define BUF_SIZE 30
void error_handling(char *message);
void urg_handler(int signo);

int acpt_sock;
int recv_sock;

int main(int argc, char *argv[]) {
    struct sockaddr_in recv_adr, serv_adr;
    int str_len, state;
    socklen_t serv_adr_sz;
    struct sigaction act;
    char buf[BUF_SIZE];

    if (argc != 2) {
        printf("usage: %s <port> \n", argv[0]);
        exit(1);
    }
    act.sa_handler = urg_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    acpt_sock = socket(PF_INET, SOCK_STREAM, 0);

    memset(&recv_adr, 0, sizeof(recv_adr));
    recv_adr.sin_family = AF_INET;
    recv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
    recv_adr.sin_port = htons(atoi(argv[1]));
    
    if (bind(acpt_sock, (struct sockaddr*)&recv_adr, sizeof(recv_adr)) == -1) {
        error_handling("bind() error!!!");
    }
    listen(acpt_sock, 5);
    serv_adr_sz = sizeof(serv_adr);
    recv_sock = accept(acpt_sock, (struct sockaddr*)&serv_adr, &serv_adr_sz);
    
    //文件描述符 recv_sock 指向的套接字引发的 SIGURG 信号处理进程变为将getpid 函数返回值用作 ID 的进程
    fcntl(recv_sock, F_SETOWN, getpid());
    
    state = sigaction(SIGURG, &act, 0);
    while ((str_len = recv(recv_sock, buf, sizeof(buf), 0)) != 0) {
        if (str_len == -1) {
            continue;
        }
        buf[str_len] = 0;
        puts(buf);
    }
    close(recv_sock);
    close(acpt_sock);
    return 0;
}
  
void urg_handler(int signo) {
    int str_len;
    char buf[BUF_SIZE];
    str_len = recv(recv_sock, buf, sizeof(buf) - 1, MSG_OOB);
    buf[str_len] = 0;
    printf("urgent message: %s \n", buf);
} 

void error_handling(char *message) {
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

处理 SIGURG 信号时必须指定处理信号的进程,getpid 函数返回调用此函数的进程 ID,指定当前进程为处理 SIGURG 信号的主体。

紧急模式原理

真正意义是督促数据接收对象尽快处理数据。

image.png 字符 0 右侧偏移量为 3 的位置有紧急指针,紧急指针指向紧急消息的下一个位置,同时向对方传递如下信息:

紧急指针指向的偏移量为 3 之前的部分就是紧急消息

image.png URG=1:载有紧急消息的数据包 URG 指针:紧急指针位于偏移量为 3 的位置 但是,无法得知紧急消息是 890 还是 90.

检查输入缓冲

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[]) {
    int acpt_sock, recv_sock;
    struct sockaddr_in acpt_adr, recv_adr;
    int str_len, state;
    socklen_t recv_adr_sz;
    char buf[BUF_SIZE];
    if (argc != 2) {
        printf("usage: %s <port>\n", argv[0]);
        exit(1);
    }
    
    acpt_sock = socket(PF_INET, SOCK_STREAM, 0);
    memset(&acpt_adr, 0, sizeof(acpt_adr));
    acpt_adr.sin_family = AF_INET;
    acpt_adr.sin_addr.s_addr = htonl(INADDR_ANY);
    acpt_adr.sin_port = htons(atoi(argv[1]));
    if (bind(acpt_sock, (struct sockaddr*)&acpt_adr, sizeof(acpt_adr)) == -1) {
        error_handling("bind() error !!!");
    }
    listen(acpt_sock, 5);
    recv_adr_sz = sizeof(recv_adr);
    recv_sock = accept(acpt_sock, (struct sockaddr*)&recv_adr, &recv_adr_sz);
    while (1) {
        //读取后不会删除输入缓冲中的数据,因此可以再次读取
        str_len = recv(recv_sock, buf, sizeof(buf) - 1, MSG_PEEK | MSG_DONTWAIT);
        if (str_len > 0) {
            break;
        }
    }
    buf[str_len] = 0;
    printf("buffering %d bytes: %s \n", str_len, buf);

    str_len = recv(recv_sock, buf, sizeof(buf) - 1, 0);
    buf[str_len] = 0;
    printf("read again: %s\n", buf);
    close(acpt_sock);
    close(recv_sock);
    return 0;
}

void error_handling(char *message) {
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

运行结果如下: image.png

readv 和 writev 函数

功能:对数据进行整合传输及发送的函数

writev:将分散在多个缓冲中的数据一并发送

sendv:由多个缓冲分别接收

ssize_t writev(int filedes, const struct iovec *iov, int iovnt);

filedes: 文件描述符
iov: 结构体数组的地址值
iovnt:第二个参数的数组长度

struct iovec

struct iovec { 
    void * iov_base; //缓冲地址
    size_t iov_len; //缓冲大小
}

image.png

其中,_len表示要发送的缓冲中的数据长度。

#include <stdio.h>
#include <sys/uio.h>

int main(int argc, char *argv[]) {
    struct iovec vec[2];
    char buf1[] = "ABCDEFG";
    char buf2[] = "1234567";
    
    int str_len;
    vec[0].iov_base = buf1;
    vec[0].iov_len = 3;
    vec[1].iov_base = buf2;
    vec[1].iov_len = 4;

    str_len = writev(1, vec, 2);
    puts("");
    printf("write bytes: %d \n", str_len);
    return 0;
}

运行结果: image.png

#include <stdio.h>
#include <sys/uio.h>

#define BUF_SIZE 100
int main(int argc, char *argv[]) {
    struct iovec vec[2];
    char buf1[BUF_SIZE] = {0,};
    char buf2[BUF_SIZE] = {0,};

    int str_len;
    vec[0].iov_base = buf1;
    vec[0].iov_len = 5;
    vec[1].iov_base = buf2;
    vec[1].iov_len = BUF_SIZE;
    
    str_len = readv(0, vec, 2);
    printf("read bytes: %d\n", str_len);
    printf("first msg: %s\n", buf1);
    printf("second msg: %s\n", buf2);
    return 0;
}

运行结果: image.png