recv
ssize_t recv(int socket, void *buffer, size_t length, int flags);
返回值
> 0 成功接收数据大小
= 0 另外一端关闭了套接字
= -1 错误,需要获取错误码errno
1.recv与read相比,只能用于socket流
2.多了一个辅助选项
MSG_OOB 带外数据 紧急指针
MSG_PEEK 偷窥缓冲区中的数据(预先读缓冲区,不将数据从缓冲区中读走)
MSG_PEEK:可以读数据,不从缓冲区中读走,利用次特点可以方便的实现按行读取数据
1.使用read函数:一个字符一个字符读,方法不好(会多次调用系统调用read方法)
2.使用recv函数+MSG_PEEK选项
提前偷窥下缓冲区,缓冲区里边有数据后,把缓冲区中的数据读到内存中
然后在内存中一个字节一个字节判断是否为\n
readline函数实现:recv+MSG_PEEK
//recv_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;
//else if(ret>0) //recv_peekt偷窥到了ret个字节的数据
nread=ret;
int i;
for(i=0;i<nread;i++){ //逐个判断读到的bufp中是否有\n
if(bufp[i]=='\n'){ //如果缓冲区中有\n
ret=readn(fd,bufp,i+1); //读走数据
if(ret!=i+1)
exit(EXIT_FAILURE);
return ret; //有\n就返回,并返回读走的字节数
}
}
if(nread>nleft) //if 读到的数 > 一行最大数 —> 异常处理
exit(EXIT_FAILURE);
nleft-=nread; //若缓冲区没有\n,把剩余的数据读走
ret=readn(fd,bufp,nread);
if(ret!=nread)
exit(EXIT_FAILURE);
bufp+=nread; //bufp指针后移后,再接着偷看缓冲区数据recv_peek,直到遇到\n
}
}
readline的使用:
while(1){
char recvbuf[1024];
int ret=readline(conn,recvbuf,1024); //readline
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);
}//while
客户端服务器回射模型
客户端代码
#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){
//1.send message
writen(sockfd,&sendbuf,strlen(sendbuf));
//2.recv message
int ret=readline(sockfd,recvbuf,sizeof(recvbuf));
if(ret<0)
ERR_EXIT("recvline");
if(ret==0){
printf("peer close\n");
close(sockfd);
break;
}
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);
/* 方法2:错误的方法
如果有5个客户端连接服务器
当5个客户端同时死掉(5个子进程将会同时死掉)--->5个子进程同时向老爹发送SIGCHLD信号
但是:SIGCHLD是不可靠信号,因此老爹可能收到1个,2个或者5个(结果不确定),因此使用
wait函数,只能清理一个子进程后就退出,--->正确地做法:使用waitpid
void handler(int signo){
printf("waitpid child process\n");
wait(NULL);
}
*/
//方法3:正确的方法
void handler(int signo){
printf("waitpid child process\n");
if(signo==SIGCHLD){
while(waitpid(-1,NULL,WNOHANG)!=-1);
}
}
int main(){
//signal(SIGCHLD,SIG_IGN); //方法1
signal(SIGCHLD,handler); //方法2 方法3
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);
break; //break后,执行exit(EXIT_SUCCESS),退出
}
printf("recvline=%s\n",recvbuf);
writen(conn,recvbuf,strlen(recvbuf));
memset(recvbuf,0,sizeof(recvbuf));
}
exit(EXIT_SUCCESS);
}
}
}