本文所有函数皆是为实现 TCP之简单回传(二) 系列所封装的函数;
所有函数皆用C语言实现。函数以及注释如下:
头文件:
//.h #ifndef SYSUTIL_H #define SYSUTIL_H #include <stdint.h> #include <sys/types.h> void nano_sleep(double val); //实现定时作用 ssize_t readn(int fd, void *buf, size_t count);//读取真实数据 ssize_t writen(int fd, const void *buf, size_t count);//写所读来的数据 ssize_t readline(int fd, void *usrbuf, size_t maxlen);//读数据(解决粘包问题) ssize_t readline_slow(int fd, void *usrbuf, size_t maxlen);//读数据--->效率低下 void send_int32(int sockfd, int32_t val);//发送一个int int32_t recv_int32(int sockfd); //接收一个int 为后来的readn读入精准的数据做准备 #endif
具体实现:
/.c #include "sysutil.h" #include <stdint.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <errno.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0) //睡眠时间 void nano_sleep(double val) { struct timespec tv; memset(&tv, 0, sizeof(tv)); tv.tv_sec = val;//取整 tv.tv_nsec = (val- tv.tv_sec)*1000*1000*1000; int ret; do { ret = nanosleep(&tv, &tv); }while(ret == -1 && errno ==EINTR);//若是被中断信号打断,则继续执行sleep } //告诉server,本次发送数据的真实长度val void send_int32(int sockfd, int32_t val) { int32_t tmp = htonl(val);//转化为网络字节序 if(writen(sockfd, &tmp, sizeof(int32_t)) != sizeof(int32_t))//发送给server ERR_EXIT("write"); } //接收一个int型数据,以确保精确接收 int32_t recv_int32(int sockfd) { int32_t tmp; if(readn(sockfd, &tmp, sizeof(int32_t))!= sizeof(int32_t)) ERR_EXIT("read"); return ntohl(tmp);//网络序转化为主机序。 } //server读取数据 ssize_t readn(int fd, void *buf, size_t count) { size_t nleft = count; //剩余字符数 ssize_t nread;//用于返回值 char *pbuf = (char*)buf; while(nleft > 0) { nread = read(fd, pbuf, nleft);//发送数据 if( nread == -1) { if(errno == EINTR)//被中断信号打断 continue; return -1 ; //err }else if( nread == 0) { break; //读完 } nleft = nleft - nread;//剩余字符数 pbuf = pbuf + nread;//下次的偏移位置 } return (count-nleft) ;//attentin 两个条件退出循环 } //client向server写数据 ssize_t writen(int fd, const void* buf, size_t count) { size_t nleft = count ;//剩余字节流 ssize_t nwrite;//return const char *pbuf =(const char*)buf; while(nleft > 0) { nwrite = write( fd, pbuf, nleft);//写数据 if(nwrite <= 0)//err { if(nwrite == -1 && errno == EINTR) continue; return -1; } nleft = nleft - nwrite;//剩余字节流 pbuf = pbuf + nwrite;//偏移位置 } return count; } //预览内核缓冲区数据 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; } //按字符读取--->由于每读取一个字符就产生一次系统调用,故效率较低 ssize_t readline_slow(int fd, void *usrbuf, size_t maxlen) { char *bufp = (char*)usrbuf;//偏移位置 ssize_t nread; size_t nleft = maxlen -1 ;//剩余字节数 char ch;//保存每次读取的字符 while( nleft > 0)//只要还有没读的,就一直循环 { if(-1 == (nread=read(fd, &ch, 1)))//把从fd中读取的字符存进ch中 { if(errno == EINTR)//被中断信号打断 continue; return -1;//err }else if(0 == nread ) break; //EOF *bufp = ch;//将读取的字符存进buf bufp++;//向前移动 nleft --;//剩余字节数-- if(ch == ' ')//如果该字符为 。本次读取完成 break; } *bufp ='