TCP之简单回传(四)

继续采用 TCP之简单回传(三) 的思路,不过由于其转型时,每次读取一个字符都要调用系统函数一次,故其效率较低;

本次我们采用系统中一个函数recv实现预读取

int PASCAL FAR recv( SOCKET s, char FAR* buf, int len, int flags);
s:一个标识已连接套接口的描述字。
buf:用于接收数据的缓冲区。
len:缓冲区长度。
flags:指定调用方式。
MSG_PEEK 查看当前数据。数据将被复制到缓冲区中,但并不从输入队列中删除。

通过预览内核缓冲区中的数据判断,我们所预览的数据中是否存在' ';
若存在,则将 ' '之前的数据(包括' ')读进我们自己的缓冲区;

若不存在,则直接将我们预览的所有数据读进缓冲区;

recv_peek 以及readn解决这个问题;

代码如下:

//预览内核缓冲区数据
ssize_t recv_peek(int fd, void *usrbuf, size_t maxlen)
{
    ssize_t nread;
    do
    {
        nread = recv(fd, usrbuf, maxlen, MSG_PEEK);        
    } 
    while(nread == -1 && errno == EINTR);
    return nread;
}

ssize_t readline(int fd, void *usrbuf, size_t maxlen)
{
    char *bufp = (char *)usrbuf;
    size_t nleft = maxlen - 1;
    ssize_t count = 0;

    ssize_t  nread;
    while(nleft > 0)
    {
        nread = recv_peek(fd, bufp, nleft);//预览内核缓冲区数据
        if( nread <= 0)  //由客户端处理
            return nread;
        //遍历bufp,以确定是否存在
 
        int i;
        for ( i = 0; i < nread; i++) 
        {
        //存在'
'    
            if(bufp[i] == '
')
            {
                size_t nsize = i +1; 
                if( readn(fd, bufp, nsize) != nsize)//说明
前有i个字符
                    ERR_EXIT("readn");
                bufp +=nsize; //重置偏移量
                count +=nsize;//统计读取个数
                *bufp = 0;
                return count;
            }
        }
        //不存在'
'
        if( readn(fd, bufp, nread) != nread)
            ERR_EXIT("readn");
        bufp += nread;
        count += nread;
        nleft -=nread;
    }
    *bufp = 0;
    return count;
}
View Code

server服务器代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define ERR_EXIT(m) 
    do { 
        perror(m);
        exit(EXIT_FAILURE);
    }while(0)

void do_server(int fd);

int main(int argc, const char *argv[])
{
//socket    
    int listenfd = socket(AF_INET, SOCK_STREAM, 0 );
    if( -1 == listenfd)
        ERR_EXIT("socket");
//地址复用
    int on = 1;
    if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) < 0)
        ERR_EXIT("setsockopt");
//bind
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888); //主机字节序转化为网络字节序
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");//点分十进制转化成网络字节序
    if( -1 == bind( listenfd, (struct sockaddr*)&addr, sizeof(addr)))
        ERR_EXIT("bind");

//listen
    if( -1 == listen(listenfd,SOMAXCONN ))
        ERR_EXIT("listen");
    
//accept    
    int peerfd = accept(listenfd, NULL, NULL);//对方的IP&PORT

//read&write    
    do_server(peerfd);

//close
    close(peerfd);
    close(listenfd);
    return 0;
}

void do_server(int  fd)
{
    char recvbuf[1024100] = {0};
    int cnt =0;
    while(1)
    {
        int nread = readline(fd, recvbuf, sizeof(recvbuf));
        if(nread == -1)//err
        {
            if(errno == EINTR)
                continue;
            ERR_EXIT("read");
        }else if (nread == 0 )//write close
        {
            printf("close...
");
            exit(EXIT_FAILURE);
        }
        
        // ok 
        printf("count = %d recv size = %d
",++cnt, nread);
        memset(recvbuf, 0, sizeof(recvbuf));
    }
}
View Code

client客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define ERR_EXIT(m) 
    do { 
        perror(m);
        exit(EXIT_FAILURE);
    }while(0)
void do_server(int fd);

int main(int argc, const char *argv[])
{
//socket
    int peerfd = socket(AF_INET, SOCK_STREAM,0);
    if( -1 == peerfd)
        ERR_EXIT("socket");
//connect
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    if( -1 == connect(peerfd,(struct sockaddr*)&addr, sizeof(addr) ))
        ERR_EXIT("connect");

//read&write
    do_server(peerfd);

    return 0;
}

void do_server(int fd)
{
    #define SIZE 1024
    char sendbuf[SIZE +1]= {0};
    int i ;
    for (i = 0; i < SIZE-1; i++) //attention
    {
        sendbuf[i]= 'a';    
    }
    sendbuf[SIZE-1] = '
';//标志位
    int cnt =0;
    while(1)
    {
        int i;
        for (i = 0; i < 10; i++) 
        {
            int nwrite =writen(fd, sendbuf, SIZE);//发送SIZE个数据中最后一个字符为

            if( nwrite != SIZE)
                ERR_EXIT("writen");
            printf("cout = %d,write %d bytes
",++cnt, SIZE);
        }
        nano_sleep(4.5);
    }
}
View Code

测试结果:

./server
count = 1 recv size = 1024
count = 2 recv size = 1024
count = 3 recv size = 1024
count = 4 recv size = 1024
count = 5 recv size = 1024
count = 6 recv size = 1024
count = 7 recv size = 1024
count = 8 recv size = 1024
count = 9 recv size = 1024
count = 10 recv size = 1024
count = 11 recv size = 1024
count = 12 recv size = 1024
count = 13 recv size = 1024
count = 14 recv size = 1024
count = 15 recv size = 1024
count = 16 recv size = 1024
count = 17 recv size = 1024
count = 18 recv size = 1024
count = 19 recv size = 1024
count = 20 recv size = 1024
View Code
./client
count = 1 recv size = 1024
count = 2 recv size = 1024
count = 3 recv size = 1024
count = 4 recv size = 1024
count = 5 recv size = 1024
count = 6 recv size = 1024
count = 7 recv size = 1024
count = 8 recv size = 1024
count = 9 recv size = 1024
count = 10 recv size = 1024
count = 11 recv size = 1024
count = 12 recv size = 1024
count = 13 recv size = 1024
count = 14 recv size = 1024
count = 15 recv size = 1024
count = 16 recv size = 1024
count = 17 recv size = 1024
count = 18 recv size = 1024
count = 19 recv size = 1024
count = 20 recv size = 1024
View Code
原文地址:https://www.cnblogs.com/xfxu/p/4027624.html