继续采用 的思路,不过由于其转型时,每次读取一个字符都要调用系统函数一次,故其效率较低;
本次我们采用系统中一个函数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;}
server服务器代码:
#include#include #include #include #include #include #include #include #include #include #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)); }}
client客户端代码:
#include#include #include #include #include #include #include #include #include #include #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); }}
测试结果:
./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
./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