首页 > 代码库 > TCP之简单回传(四)

TCP之简单回传(四)

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

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

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

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

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

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,以确定是否存在\n         int i;        for ( i = 0; i < nread; i++)         {        //存在‘\n‘                if(bufp[i] == \n)            {                size_t nsize = i +1;                 if( readn(fd, bufp, nsize) != nsize)//说明\n前有i个字符                    ERR_EXIT("readn");                bufp +=nsize; //重置偏移量                count +=nsize;//统计读取个数                *bufp = 0;                return count;            }        }        //不存在‘\n‘        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...\n");            exit(EXIT_FAILURE);        }                // ok         printf("count = %d recv size = %d\n",++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] = \n;//标志位    int cnt =0;    while(1)    {        int i;        for (i = 0; i < 10; i++)         {            int nwrite =writen(fd, sendbuf, SIZE);//发送SIZE个数据中最后一个字符为\n            if( nwrite != SIZE)                ERR_EXIT("writen");            printf("cout = %d,write %d bytes\n",++cnt, SIZE);        }        nano_sleep(4.5);    }}
View Code

测试结果:

./servercount = 1 recv size = 1024count = 2 recv size = 1024count = 3 recv size = 1024count = 4 recv size = 1024count = 5 recv size = 1024count = 6 recv size = 1024count = 7 recv size = 1024count = 8 recv size = 1024count = 9 recv size = 1024count = 10 recv size = 1024count = 11 recv size = 1024count = 12 recv size = 1024count = 13 recv size = 1024count = 14 recv size = 1024count = 15 recv size = 1024count = 16 recv size = 1024count = 17 recv size = 1024count = 18 recv size = 1024count = 19 recv size = 1024count = 20 recv size = 1024
View Code
./clientcount = 1 recv size = 1024count = 2 recv size = 1024count = 3 recv size = 1024count = 4 recv size = 1024count = 5 recv size = 1024count = 6 recv size = 1024count = 7 recv size = 1024count = 8 recv size = 1024count = 9 recv size = 1024count = 10 recv size = 1024count = 11 recv size = 1024count = 12 recv size = 1024count = 13 recv size = 1024count = 14 recv size = 1024count = 15 recv size = 1024count = 16 recv size = 1024count = 17 recv size = 1024count = 18 recv size = 1024count = 19 recv size = 1024count = 20 recv size = 1024
View Code

 

TCP之简单回传(四)