一.为什么使用recv实现readline函数
- 使用read函数:一个字符一个字符读,方法不好(会多次调用系统调用read方法)
- 使用recv函数+MSG_PEEK选项
提前偷窥下缓冲区,缓冲区里边有数据后,把缓冲区中的数据读到内存中然后在内存中一个字节一个字节判断是否为\n
二.recv函数介绍
ssize_t recv(int socket, void *buffer, size_t length, int flags);
返回值
> 0 成功接收数据大小
= 0 另外一端关闭了套接字
= -1 错误,需要获取错误码errno
参数:
flags
MSG_OOB 带外数据 紧急指针
MSG_PEEK 偷窥缓冲区中的数据(预先读缓冲区,不将数据从缓冲区中读走)
注意:recv只能用于socket流;read可以用于socket流和文件
三. readline函数实现:recv+MSG_PEEK
ssize_t recv_peek(int fd,void* buf,size_t len){
while(1){
int ret=recv(fd,buf,len,MSG_PEEK);
if(ret==-1 && errno==EINTR)
continue;
return ret;
}
}
ssize_t readline(int fd,void* buf,size_t maxLine){
int ret;
int nread;
char* bufp=(char*)buf;
int nleft=maxLine;
while(1){
ret=recv_peek(fd,bufp,nleft);
if(ret<0)
return ret;
else if(ret==0)
return ret;
nread=ret;
int i;
for(i=0;i<nread;i++){
if(bufp[i]=='\n'){
ret=readn(fd,bufp,i+1);
if(ret!=i+1)
exit(EXIT_FAILURE);
return ret;
}
}
if(nread>nleft)
exit(EXIT_FAILURE);
nleft-=nread;
ret=readn(fd,bufp,nread);
if(ret!=nread)
exit(EXIT_FAILURE);
bufp+=nread;
}
}
四.readline的使用
while(1){
char recvbuf[1024];
int ret=readline(conn,recvbuf,1024);
if(ret<0)
ERR_EXIT("readline");
else if(ret==0){
printf("peer close\n");
close(conn);
break;
}
else if(ret>0)
printf("recvline=%s\n",recvbuf);
}
五.示例代码(客户端服务器回射模型)
客户端代码
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0);
ssize_t readn(int fd,void* buf,size_t count);
ssize_t writen(int fd,void* buf,size_t count);
ssize_t recv_peek(int fd,void* buf,size_t len);
ssize_t readline(int fd,void* buf,size_t maxLine);
int main(){
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
ERR_EXIT("socket");
struct sockaddr_in svraddr;
svraddr.sin_family=AF_INET;
svraddr.sin_port=htons(8001);
svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
if(connect(sockfd,(struct sockaddr*)&svraddr,sizeof(struct sockaddr))<0)
ERR_EXIT("connect");
printf("connect svr success:svraddr=%s,port=%d\n",inet_ntoa(svraddr.sin_addr),ntohs(svraddr.sin_port));
char sendbuf[1024];
char recvbuf[1024];
printf("send=");
while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL){
writen(sockfd,&sendbuf,strlen(sendbuf));
int ret=readline(sockfd,recvbuf,sizeof(recvbuf));
if(ret<0)
ERR_EXIT("recvline");
if(ret==0){
printf("peer close\n");
close(sockfd);
exit(EXIT_SUCCESS);
}
else if(ret>0)
printf("recvline=%s",recvbuf);
memset(&recvbuf,0,sizeof(recvbuf));
memset(&sendbuf,0,sizeof(sendbuf));
printf("send=");
}
return 0;
}
服务器代码
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0);
ssize_t readn(int fd,void* buf,size_t count);
ssize_t writen(int fd,void* buf,size_t count);
ssize_t recv_peek(int fd,void* buf,size_t len);
ssize_t readline(int fd,void* buf,size_t maxLine);
void handler(int signo){
printf("waitpid child process\n");
if(signo==SIGCHLD){
while(waitpid(-1,NULL,WNOHANG)!=-1);
}
}
int main(){
signal(SIGCHLD,handler);
int listenfd;
if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0)
ERR_EXIT("socket");
struct sockaddr_in svraddr;
svraddr.sin_family=AF_INET;
svraddr.sin_port=htons(8001);
svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
int on=1;
if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
ERR_EXIT("setsockopt");
if(bind(listenfd,(struct sockaddr*)&svraddr,sizeof(struct sockaddr))<0)
ERR_EXIT("bind");
if(listen(listenfd,SOMAXCONN)<0)
ERR_EXIT("listen");
printf("listen...\n");
struct sockaddr_in peeraddr;
socklen_t peerlen;
int conn;
while(1){
if((conn=accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen))<0)
ERR_EXIT("accept");
printf("cli connect success\n");
pid_t pid=fork();
if(pid==-1){
ERR_EXIT("fork");
}
else if(pid>0){
close(conn);
}
else if(pid==0){
while(1){
char recvbuf[1024];
int ret=readline(conn,recvbuf,1024);
if(ret<0)
ERR_EXIT("readline");
if(ret==0){
printf("peer close\n");
close(conn);
exit(EXIT_SUCCESS);
}
printf("recvline=%s\n",recvbuf);
writen(conn,recvbuf,strlen(recvbuf));
memset(recvbuf,0,sizeof(recvbuf));
}
}
}
}