Linux网络编程--IO函数以及示例

网络数据能够正确到达用户并被用户接收是进行网络数据传输的基本目的, 网络数据的接受和发送有很多种方案,例如:直接发送和接收,通过向量发送和接收,使用消息发送和接收等。本篇文章主要介绍常用的IO函数以及用法,如:最常用的read()/write()函数,和其他标准的套接字专用函数recv()/send(),readv()/writev(),recvmsg()/sendmsg()。
各个函数原型以及介绍如下:

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
//以上两个函数不再介绍,因为太常用了。


ssize_t recv(int sockfd, void *buf, size_t len, int flags);
//recv()函数从套接字sockfd中接收数据放在缓冲区buf中,buf长度是len(以字节为单位),操作方式由flags指定,
//常用的flags有MSG_DONTWAIT(非阻塞操作立即返回)、MSG_OOB(专用于接收带外数据,不接收一般数据)、MSG_WAITALL(此标志告诉内核在没有读到请求的数据之前不不使读操作返回)、
//MSG_TRUNC(接收到数据之后,如果数据缓冲区不足以容纳接受的数据是会截取数据,超出数据将被丢弃)。
//返回值:成功接收的字节数,返回-1表示有错误,可以查看错误码,返回0说明另一方使用正常方式关闭连接如close()
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
//send()函数将缓冲区buf大小为len的数据,通过套接字描述符sockfd按照flags指定的方式发送出去。
//通常send不一定一次发送完成,需要检查其返回值是否已经发送完毕。
//返回值:成功发送的字节数,返回-1表示有错误,可以查看错误码,返回0说明另一方使用正常方式关闭连接如close()


ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
//readv()函数从套接字fd中读取iovcnt块数据到缓冲区向量iov中,返回值:成功接收的字节数,返回-1表示有错误,可以查看错误码。
struct iovec{
    void *iov_base;  /*向量缓冲区地址*/
    size_t iov_len;  /*向量缓冲区大小,字节为单位*/
};
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
//writev函数向套接字fd中写入在向量iov中保存在iovcnt块数据。
//返回值:成功接收的字节数,返回-1表示有错误,可以查看错误码。


ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
//recvmsg()函数从套接字sockfd中接收数据放在缓冲区msg中,msg长度是len(以字节为单位),操作方式由flags指定
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
//sendmsg()函数向套接字描述符sockfd中按照结构msg的设定写入数据,操作方式由flags指定。
//两者区别:recvmsg()和sendmsg()函数区别在于recvmsg()函数操作方式由参数msg结构中成员变量msg_flags指定,
//而sendmsg()函数由flags指定。

总结上述IO函数的区别:
这里写图片描述

下面用精辟的代码进行讲解:

/*tcp_client.c    客户端代码框架*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/in.h>
#include <signal.h>

extern void sig_proccess(int signo);
extern void sig_pipe(int signo);
static int s;
void sig_proccess_client(int signo)
{
    printf("Catch a exit signal\n");
    close(s);
    exit(0);    
}

#define PORT 8888   /* 侦听端口地址 */
int main(int argc, char *argv[])
{

    struct sockaddr_in server_addr; /* 服务器地址结构 */
    int err;/* 返回值 */

    if(argc == 1){
        printf("PLS input server addr\n");
        return 0;   
    }
    signal(SIGINT, sig_proccess);
    signal(SIGPIPE, sig_pipe);

    /* 建立一个流式套接字 */
    s = socket(AF_INET, SOCK_STREAM, 0);
    if(s < 0){/* 出错 */
        printf("socket error\n");
        return -1;  
    }   

    /* 设置服务器地址 */
    bzero(&server_addr, sizeof(server_addr));       /* 清0 */
    server_addr.sin_family = AF_INET;               /* 协议族 */
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);/* 本地地址 */
    server_addr.sin_port = htons(PORT);             /* 服务器端口 */

    /* 将用户输入的字符串类型的IP地址转为整型 */
    inet_pton(AF_INET, argv[1], &server_addr.sin_addr); 
    /* 连接服务器 */
    connect(s, (struct sockaddr*)&server_addr, sizeof(struct sockaddr));
    process_conn_client(s); /* 客户端处理过程 */
    close(s);   /* 关闭连接 */
}

/*tcp_server.c   服务器代码框架*/

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/in.h>
#include <signal.h>

extern void sig_proccess(int signo);

#define PORT 8888       /* 侦听端口地址 */
#define BACKLOG 2       /* 侦听队列长度 */
int main(int argc, char *argv[])
{
    int ss,sc;      /* ss为服务器的socket描述符,sc为客户端的socket描述符 */
    struct sockaddr_in server_addr; /* 服务器地址结构 */
    struct sockaddr_in client_addr; /* 客户端地址结构 */
    int err;    /* 返回值 */
    pid_t pid;  /* 分叉的进行id */

    signal(SIGINT, sig_proccess);
    signal(SIGPIPE, sig_proccess);


    /* 建立一个流式套接字 */
    ss = socket(AF_INET, SOCK_STREAM, 0);
    if(ss < 0){/* 出错 */
        printf("socket error\n");
        return -1;  
    }   

    /* 设置服务器地址 */
    bzero(&server_addr, sizeof(server_addr));   /* 清0 */
    server_addr.sin_family = AF_INET;           /* 协议族 */
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);/* 本地地址 */
    server_addr.sin_port = htons(PORT);         /* 服务器端口 */

    /* 绑定地址结构到套接字描述符 */
    err = bind(ss, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if(err < 0){/* 出错 */
        printf("bind error\n");
        return -1;  
    }

    /* 设置侦听 */
    err = listen(ss, BACKLOG);
    if(err < 0){/* 出错 */
        printf("listen error\n");
        return -1;  
    }

    /* 主循环过程 */
    for(;;) {
        int addrlen = sizeof(struct sockaddr);
        /* 接收客户端连接 */
        sc = accept(ss, (struct sockaddr*)&client_addr, &addrlen);
        if(sc < 0){     /* 出错 */
            continue;   /* 结束本次循环 */
        }   

        /* 建立一个新的进程处理到来的连接 */
        pid = fork();       /* 分叉进程 */
        if( pid == 0 ){     /* 子进程中 */
            close(ss);      /* 在子进程中关闭服务器的侦听 */
            process_conn_server(sc);/* 处理连接 */
        }else{
            close(sc);      /* 在父进程中关闭客户端的连接 */
        }
    }
}
/*tcp_process.c    
包含使用各种IO函数的例子,主要根据#ifdefine条件
判断使用的是哪一个IO函数,然后进行编译执行*/

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

#define RECVSEND
//#define READVWRITEV
//#define RECVMSGSENDMSG

#ifdef RECVSEND


/* 服务器对客户端的处理 */
void process_conn_server(int s)
{
    ssize_t size = 0;
    char buffer[1024];  /* 数据的缓冲区 */

    for(;;){/* 循环处理过程 */
        /* 从套接字中读取数据放到缓冲区buffer中 */
        size = recv(s, buffer, 1024,0); 
        if(size == 0){/* 没有数据 */
            return; 
        }

        /* 构建响应字符,为接收到客户端字节的数量 */
        sprintf(buffer, "%d bytes altogether\n", size);
        send(s, buffer, strlen(buffer)+1,0);/* 发给客户端 */
    }   
}

/* 客户端的处理过程 */
void process_conn_client(int s)
{
    ssize_t size = 0;
    char buffer[1024];  /* 数据的缓冲区 */

    for(;;){/* 循环处理过程 */
        /* 从标准输入中读取数据放到缓冲区buffer中 */
        size = read(0, buffer, 1024);
        if(size > 0){/* 读到数据 */
            send(s, buffer, size,0);        /* 发送给服务器 */
            size = recv(s, buffer, 1024,0);/* 从服务器读取数据 */
            write(1, buffer, size);     /* 写到标准输出 */
        }
    }   
}

void sig_proccess(int signo)
{
    printf("Catch a exit signal\n");
    _exit(0);   
}

void sig_pipe(int sign)
{
    printf("Catch a SIGPIPE signal\n");

    /* 释放资源 */  
}
#endif

#ifdef READVWRITEV
#include <sys/uio.h>
#include <string.h>
#include <stdlib.h>

static struct iovec *vs=NULL,*vc=NULL;
/* 服务器对客户端的处理 */
void process_conn_server(int s)
{
    char buffer[30];    /* 向量的缓冲区 */
    ssize_t size = 0;
    /* 申请3个向量 */
    struct iovec *v = (struct iovec*)malloc(3*sizeof(struct iovec));
    if(!v){
        printf("Not enough memory\n");
        return;     
    }

    /*挂接全局变量,便于释放管理*/
    vs = v;

    /* 每个向量10个字节的空间 */
    v[0].iov_base = buffer; /*0-9*/
    v[1].iov_base = buffer + 10;/*10-19*/
    v[2].iov_base = buffer + 20;/*20-29*/
    /*初始化长度为10*/
    v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;



    for(;;){/* 循环处理过程 */
        /* 从套接字中读取数据放到向量缓冲区中 */
        size = readv(s, v, 3);  
        if(size == 0){/* 没有数据 */
            return; 
        }

        /* 构建响应字符,为接收到客户端字节的数量,分别放到三个缓冲区中 */
        sprintf(v[0].iov_base, "%d ", size); /*长度*/
        sprintf(v[1].iov_base, "bytes alt"); /*“bytes alt”字符串*/
        sprintf(v[2].iov_base, "ogether\n"); /*ogether\n”字符串*/
        /*写入字符串长度*/
        v[0].iov_len = strlen(v[0].iov_base);
        v[1].iov_len = strlen(v[1].iov_base);
        v[2].iov_len = strlen(v[2].iov_base);
        writev(s, v, 3);/* 发给客户端 */
    }   
}

/* 客户端的处理过程 */
void process_conn_client(int s)
{
    char buffer[30];/* 向量的缓冲区 */
    ssize_t size = 0;
    /* 申请3个向量 */
    struct iovec *v = (struct iovec*)malloc(3*sizeof(struct iovec));
    if(!v){
        printf("Not enough memory\n");
        return;     
    }
    /**挂接全局变量,便于释放管理*/
    vc = v;
    /* 每个向量10个字节的空间 */
    v[0].iov_base = buffer;/*0-9*/
    v[1].iov_base = buffer + 10;/*10-19*/
    v[2].iov_base = buffer + 20;/*20-29*/
    /*初始化长度为10*/
    v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;

    int i = 0;
    for(;;){/* 循环处理过程 */
        /* 从标准输入中读取数据放到缓冲区buffer中 */
        size = read(0, v[0].iov_base, 10);
        if(size > 0){/* 读到数据 */
            v[0].iov_len= size;
            writev(s, v,1);     /* 发送给服务器 */
            v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;
            size = readv(s, v, 3);/* 从服务器读取数据 */
            for(i = 0;i<3;i++){
                if(v[i].iov_len > 0){
                    write(1, v[i].iov_base, v[i].iov_len);      /* 写到标准输出 */
                }
            }
        }
    }   
}

void sig_proccess(int signo)
{
    printf("Catch a exit signal\n");
    /* 释放资源 */  
    free(vc);
    free(vs);
    _exit(0);   
}

void sig_pipe(int sign)
{
    printf("Catch a SIGPIPE signal\n");

    /* 释放资源 */  
    free(vc);
    free(vs);
    _exit(0);
}
#endif
#ifdef RECVMSGSENDMSG
#include <sys/uio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
static struct iovec *vs=NULL,*vc=NULL;
/* 服务器对客户端的处理 */
void process_conn_server(int s)
{
    char buffer[30];    /* 向量的缓冲区 */
    ssize_t size = 0;
    struct msghdr msg;  /* 消息结构 */


    /* 申请3个向量 */
    struct iovec *v = (struct iovec*)malloc(3*sizeof(struct iovec));
    if(!v){
        printf("Not enough memory\n");
        return;     
    }

    /**挂接全局变量,便于释放管理*/
    vs = v;

    /* 初始化消息 */
    msg.msg_name = NULL;    /*没有名字域*/
    msg.msg_namelen = 0;    /*名字域长度为0*/
    msg.msg_control = NULL; /*没有控制域*/
    msg.msg_controllen = 0; /*控制域长度为0*/
    msg.msg_iov = v;        /*挂接向量指针*/
    msg.msg_iovlen = 30;    /*接收缓冲区长度为30*/
    msg.msg_flags = 0;      /*无特殊操作*/

    /* 每个向量10个字节的空间 */
    v[0].iov_base = buffer; /*0-9*/
    v[1].iov_base = buffer + 10;/*10-19*/
    v[2].iov_base = buffer + 20;/*20-29*/
    /*初始化长度为10*/
    v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;

    for(;;){/* 循环处理过程 */
        /* 从套接字中读取数据放到向量缓冲区中 */
        size = recvmsg(s, &msg, 0); 
        if(size == 0){/* 没有数据 */
            return; 
        }

        /* 构建响应字符,为接收到客户端字节的数量,分别放到三个缓冲区中 */
        sprintf(v[0].iov_base, "%d ", size); /*长度*/
        sprintf(v[1].iov_base, "bytes alt"); /*“bytes alt”字符串*/
        sprintf(v[2].iov_base, "ogether\n"); /*ogether\n”字符串*/
        /*写入字符串长度*/
        v[0].iov_len = strlen(v[0].iov_base);
        v[1].iov_len = strlen(v[1].iov_base);
        v[2].iov_len = strlen(v[2].iov_base);
        sendmsg(s, &msg, 0);/* 发给客户端 */
    }   
}

/* 客户端的处理过程 */
void process_conn_client(int s)
{
    char buffer[30];    /* 向量的缓冲区 */
    ssize_t size = 0;
    struct msghdr msg;  /* 消息结构 */

    /* 申请3个向量 */
    struct iovec *v = (struct iovec*)malloc(3*sizeof(struct iovec));
    if(!v){
        printf("Not enough memory\n");
        return;     
    }

    /**挂接全局变量,便于释放管理*/
    vc = v;
    /* 初始化消息 */
    msg.msg_name = NULL;    /*没有名字域*/
    msg.msg_namelen = 0;    /*名字域长度为0*/
    msg.msg_control = NULL; /*没有控制域*/
    msg.msg_controllen = 0; /*控制域长度为0*/
    msg.msg_iov = v;        /*挂接向量指针*/
    msg.msg_iovlen = 30;    /*接收缓冲区长度为30*/
    msg.msg_flags = 0;      /*无特殊操作*/

    /* 每个向量10个字节的空间 */
    v[0].iov_base = buffer;/*0-9*/
    v[1].iov_base = buffer + 10;/*10-19*/
    v[2].iov_base = buffer + 20;/*20-29*/
    /*初始化长度为10*/
    v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;

    int i = 0;
    for(;;){/* 循环处理过程 */
        /* 从标准输入中读取数据放到缓冲区buffer中 */
        size = read(0, v[0].iov_base, 10);
        if(size > 0){/* 读到数据 */
            v[0].iov_len= size;
            sendmsg(s, &msg,0);     /* 发送给服务器 */
            v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;
            size = recvmsg(s, &msg,0);/* 从服务器读取数据 */
            for(i = 0;i<3;i++){
                if(v[i].iov_len > 0){
                    write(1, v[i].iov_base, v[i].iov_len);      /* 写到标准输出 */
                }
            }
        }
    }   
}

void sig_proccess(int signo)
{
    printf("Catch a exit signal\n");
    /* 释放资源 */  
    free(vc);
    free(vs);
    _exit(0);   
}

void sig_pipe(int sign)
{
    printf("Catch a SIGPIPE signal\n");

    /* 释放资源 */  
    free(vc);
    free(vs);
    _exit(0);
}
#endif
/*以下是makefile文件*/

CC = gcc
all:client server

client:tcp_process.o tcp_client.o
        $(CC) -o client tcp_process.o tcp_client.o -g
server:tcp_process.o tcp_server.o
        $(CC) -o server tcp_process.o tcp_server.o -g
tcp_process.o:
        $(CC) -c -o tcp_process.o tcp_process.c -g
clean:
        rm -f client server *.o
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux多线程服务端编程:使用muduo C++网络库》主要讲述采用现代C++在x86-64 Linux上编写多线程TCP网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread。 目 录 第1部分C++ 多线程系统编程 第1章线程安全的对象生命期管理3 1.1当析构函数遇到多线程. . . . . . . . . . . . . . . . .. . . . . . . . . . . 3 1.1.1线程安全的定义. . . . . . . . . . . . . . . . .. . . . . . . . . . . 4 1.1.2MutexLock 与MutexLockGuard. . . . . . . . . . . . . . . . . . . . 4 1.1.3一个线程安全的Counter 示例.. . . . . . . . . . . . . . . . . . . 4 1.2对象的创建很简单. . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . 5 1.3销毁太难. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . 7 1.3.1mutex 不是办法. . . . . . . . . . . . . . . . . . . .. . . . . . . . 7 1.3.2作为数据成员的mutex 不能保护析构.. . . . . . . . . . . . . . 8 1.4线程安全的Observer 有多难.. . . . . . . . . . . . . . . . . . . . . . . . 8 1.5原始指针有何不妥. . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . 11 1.6神器shared_ptr/weak_ptr . . . . . . . . . .. . . . . . . . . . . . . . . . 13 1.7插曲:系统地避免各种指针错误. . . . . . . . . . . . . . . . .. . . . . . 14 1.8应用到Observer 上.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.9再论shared_ptr 的线程安全.. . . . . . . . . . . . . . . . . . . . . . . . 17 1.10shared_ptr 技术与陷阱. . . .. . . . . . . . . . . . . . . . . . . . . . . . 19 1.11对象池. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . 21 1.11.1enable_shared_from_this . . . . . . . . . . . . . . . . . . . . . . 23 1.11.2弱回调. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . 24 1.12替代方案. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . 26 1.13心得与小结. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . 26 1.14Observer 之谬. . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 第2章线程同步精要 2.1互斥器(mutex). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.1.1只使用非递归的mutex . . . . . . . . . . . . . .. . . . . . . . . . 33 2.1.2死锁. . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . 35 2.2条件变量(condition variable). . . . . . . . . .

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值