版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wk_bjut_edu_cn/article/details/82319051
函数原型
#include <sys/socket.h>
ssize_t recv(int sckfd,void *buff,size_t nbytes,int flags);
ssize_t send(int sckfd,const void *buff,size_t nbytes,int flags);
返回值
若成功则为读入或写出的字节数,若出错则为-1。
参数说明
recv和send的前三个参数等同于read和write的3个参数。flags参数值或为0,或者为常量值的逻辑或,最常用的是:
flags | 说明 | recv | send |
MSG_OOB | 发送或接收带外数据 | 有 | 有 |
MSG_PPEK | 窥看外来消息 | 有 | 无 |
recv和send只能用于套接口IO,不能用于文件IO以及其它的IO,而read、write可以用于任意的IO
利用recv实现readline的功能,readline函数能够实现按行读取,直到遇到\n为止,说明这算是一条消息。这也就能够解决粘包问题
服务端
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
ssize_t readn(int fd,void *buf,size_t count)
{
size_t nleft=count;
ssize_t nread;
char *bufp=(char*)buf;
while(nleft>0)
{
if((nread=read(fd,bufp,nleft))<0)
{
if(errno==EINTR)
continue;
return -1;
}
else if(nread==0)
return count-nleft;
bufp+=nread;
nleft-=nread;
}
return count;
}
ssize_t writen(int fd,void *buf,size_t count)
{
size_t nleft=count;
ssize_t nwritten;
char *bufp=(char*)buf;
while(nleft>0)
{
if((nwritten=write(fd,bufp,nleft))<0)
{
if(errno==EINTR)
continue;
return -1;
}
else if(nwritten==0)
continue;
bufp+=nwritten;
nleft-=nwritten;
}
return count;
}
//去看客户端的讲解
ssize_t recv_peek(int sockfd,void *buf,size_t len)
{
while(1)
{
int ret=recv(sockfd,buf,len,MSG_PEEK);
if(ret==-1 && errno==EINTR)
continue;
return ret;
}
}
//去看客户端的讲解
ssize_t readline(int sockfd,void *buf,size_t maxline)
{
int ret;
int nread;
char *bufp=buf;
int nleft=maxline;
while(1)
{
ret=recv_peek(sockfd,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(sockfd,bufp,i+1);
if(ret!=i+1)
exit(EXIT_FAILURE);
return ret;
}
}
if(nread>nleft)
exit(EXIT_FAILURE);
nleft-=nread;
ret=readn(sockfd,bufp,nread);
if(ret!=nread)
exit(EXIT_FAILURE);
bufp+=nread;
}
return -1;
}
void do_service(int conn)
{
char recvbuf[1024];
while(1)
{
memset(recvbuf,0,sizeof(recvbuf));
int ret=readline(conn,&recvbuf,1024);
if(ret==-1)
ERR_EXIT("readline");
if(ret==0)
{
printf("client close\n");
break;
}
fputs(recvbuf,stdout);
writen(conn,recvbuf,strlen(recvbuf));
}
}
int main(void)
{
int listenfd;
if((listenfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
/*if((listenfd=socket(PF_INET,SOCK_STREAM,0))<0) */
ERR_EXIT("socket");
struct sockaddr_in servaddr;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(5188);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
/*servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");*/
/*inet_aton("127.0.0.1,&servaddr.sin_addr");*/
int on=1;
if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
ERR_EXIT("setsockopt");
if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
ERR_EXIT("bind");
if(listen(listenfd,SOMAXCONN)<0)
ERR_EXIT("listen");
struct sockaddr_in peeraddr;
socklen_t peerlen=sizeof(peeraddr);
int conn;
pid_t pid;
while(1)
{
if((conn=accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen))<0)
ERR_EXIT("accept");
printf("ip=%s port=%d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
pid=fork();
if(pid==-1)
ERR_EXIT("fork");
if(pid==0)
{
close(listenfd);
do_service(conn);
exit(EXIT_SUCCESS);
}
else
close(conn);
}
return 0;
}
客户端
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
ssize_t readn(int fd,void *buf,size_t count)
{
size_t nleft=count;
ssize_t nread;
char *bufp=(char*)buf;
while(nleft>0)
{
if((nread=read(fd,bufp,nleft))<0)
{
if(errno==EINTR)
continue;
return -1;
}
else if(nread==0)
return count-nleft;
bufp+=nread;
nleft-=nread;
}
return count;
}
ssize_t writen(int fd,void *buf,size_t count)
{
size_t nleft=count;
ssize_t nwritten;
char *bufp=(char*)buf;
while(nleft>0)
{
if((nwritten=write(fd,bufp,nleft))<0)
{
if(errno==EINTR)
continue;
return -1;
}
else if(nwritten==0)
continue;
bufp+=nwritten;
nleft-=nwritten;
}
return count;
}
//这个函数能够从套接口接收数据,通过MSG_PEEK指定并不将数据从缓冲移除
//并不说一定要接收len个字符,只要偷看到数据就返回,没有数据就阻塞
ssize_t recv_peek(int sockfd,void *buf,size_t len)
{
while(1)
{
//它仅仅只是从套接口缓冲区当中接收数据到buf当中,
//并没有将数据从套接口缓冲区移除,而read函数在接收的同时将数据移除了
int ret=recv(sockfd,buf,len,MSG_PEEK);
if(ret==-1 && errno==EINTR)
continue;
return ret;
}
}
/*
实现的是按行读取,也就是读取知道遇到\n为止,这算是一条消息
这种方法也可以解决粘包问题,通过\n表明了消息与消息之间的边界
*/
//只能用于套接口,因为是用recv来实现的
//maxline:一行最大的字节数,读取的时候不是必须读maxline,
//遇到\n就停止了,maxline只是最大限度
ssize_t readline(int sockfd,void *buf,size_t maxline)
{
int ret;
//接收到的字节数
int nread;
char *bufp=buf;
int nleft=maxline;
while(1)
{
ret=recv_peek(sockfd,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')
{
//刚刚的recv_peek只是偷窥了一下缓冲区的数据,并没有将其移走,
//可以使用readn方法将数据从缓冲区移除,读走这些数据
ret=readn(sockfd,bufp,i+1);
if(ret!=i+1)
exit(EXIT_FAILURE);
return ret;
}
}
//下面这些是没有遇到\n时的处理,读走偷窥出来的部分,然后继续偷窥
if(nread>nleft)
exit(EXIT_FAILURE);
nleft-=nread;
//nread里面还没有遇到换行符,也把它从套接口缓冲区移除
ret=readn(sockfd,bufp,nread);
if(ret!=nread)
exit(EXIT_FAILURE);
//下一次继续偷窥,直到遇到\n为止
bufp+=nread;
}
//程序运行到这个位置就是出错了
return -1;
}
int main(void)
{
int sock;
if((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
/*if((listenfd=socket(PF_INET,SOCK_STREAM,0))<0) */
ERR_EXIT("socket");
struct sockaddr_in servaddr;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(5188);
servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
/*inet_aton("127.0.0.1,&servaddr.sin_addr");*/
if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
ERR_EXIT("connect");
//本地的地址
struct sockaddr_in localaddr;
socklen_t addrlen=sizeof(localaddr);
//getsockname()获得本地地址
if(getsockname(sock,(struct sockaddr*)&localaddr,&addrlen)<0)
ERR_EXIT("getsockname");
printf("ip=%s port=%d\n",inet_ntoa(localaddr.sin_addr),ntohs(localaddr.sin_port));
char sendbuf[1024]={0};
char recvbuf[1024]={0};
while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL)
{
writen(sock,sendbuf,strlen(sendbuf));
int ret=readline(sock,recvbuf,sizeof(recvbuf));
if(ret==-1)
ERR_EXIT("readline");
else if(ret==0)
{
printf("client close\n");
break;
}
fputs(recvbuf,stdout);
memset(sendbuf,0,sizeof(sendbuf));
memset(recvbuf,0,sizeof(recvbuf));
}
close(sock);
return 0;
}