自己封装一个readline函数实现服务器客户端回射

实现的功能:一次只能读取一行,客户端输入之后,一回车,马上字符串传到服务器端并显示在终端,然后服务器端将字符串又传回给客户端。

      服务器端可以接收多个客户端的连接请求,并fork一个子进程来进行服务。

(1)封装一个只能访问套接字描述符的readline函数

(2)服务器端启动SO_REUSEADDR套接字选项,以便服务器端不必等待TIME_WAIT状态

这是服务器端代码:

  1 #include<unistd.h>
  2 #include<sys/types.h>
  3 #include<sys/socket.h>
  4 #include<netinet/in.h>
  5 #include<arpa/inet.h>
  6 #include<stdlib.h>
  7 #include<stdio.h>
  8 #include<errno.h>
  9 #include<string.h>
 10 
 11 #define ERR_EXIT(m) 
 12         do 
 13         { 
 14             perror(m); 
 15             exit(EXIT_FAILURE);
 16         }while(0)
 17 
 18 void do_service(int conn)
 19 {
 20     char recvbuf[1024];
 21     while(1)
 22     {
 23         memset(recvbuf,0,sizeof(recvbuf));
 24         int ret=readline(conn,recvbuf,sizeof(recvbuf));
 25         if(-1==ret)
 26             ERR_EXIT("readline in do_servece");
 27         else if(0==ret)
 28         {
 29             printf("client close
");
 30             break;
 31         }
 32         fputs(recvbuf,stdout);
 33         writen(conn,recvbuf,strlen(recvbuf));
 34     }
 35 }
 36             
 37 ssize_t readn(int fd,void *buf,size_t count)
 38 {
 39     size_t nleft=count;
 40     ssize_t nread;
 41     char*bufp=(char*)buf;
 42     while(nleft>0)
 43     {
 44         if((nread=read(fd,bufp,nleft))<0)
 45         {
 46             if(errno==EINTR)
 47                 continue;
 48             return -1;
 49         }
 50         else if(nread==0)
 51             return count-nleft;
 52         bufp+=nread;
 53         nleft-=nread;
 54     }
 55     return count;
 56 }
 57 
 58 ssize_t writen(int fd,const void*buf,size_t count)
 59 {
 60     size_t nleft=count;
 61     ssize_t nwritten;
 62     char*bufp=(char*)buf;
 63     while(nleft>0)
 64     {
 65         if((nwritten=write(fd,bufp,nleft))<0)
 66         {
 67             if(errno==EINTR)
 68                 continue;
 69             return -1;
 70         }
 71         else if(nwritten==0)
 72             continue;
 73         bufp+=nwritten;
 74         nleft-=nwritten;
 75     }
 76     return count;
 77 }
 78 
 79 ssize_t recv_peek(int sockfd,void *buf,size_t len)
 80 {
 81     while(1)
 82     {
 83         int ret=recv(sockfd,buf,len,MSG_PEEK);//窥看,不清除缓存中的数据
 84         if(-1==ret&&EINTR==errno)
 85             continue;
 86         return ret;
 87     }
 88 }
 89 
 90 ssize_t readline(int sockfd,void* buf,size_t maxline)
 91 {
 92     int ret;
 93     int nread;
 94     char* bufp=(char*)buf;
 95     int nleft=maxline;
 96     while(1)
 97     {
 98         ret=recv_peek(sockfd,bufp,nleft);
 99         if(ret<0)
100             return ret;
101         else if(0==ret)
102             return ret;
103         nread=ret;
104         int i;
105         for(i=0;i<nread;i++)//读取到'
'时就应该结束
106         {
107             if(bufp[i]=='
')
108             {
109                 ret=readn(sockfd,bufp,i+1);
110                 if(ret!=i+1)
111                     exit(EXIT_FAILURE);
112                 return ret;
113             }
114         }
115         //执行到了这儿,说明没有读取到'
'
116         if(nread>nleft)
117             exit(EXIT_FAILURE);
118         nleft-=nread;
119         ret=readn(sockfd,bufp,nread);//清空缓存中的数据,因为之前是窥视,并没有清空缓存
120         if(ret!=nread)
121             exit(EXIT_FAILURE);
122         bufp+=nread;//指针后移,接着去执行while循环,确保数据追加到原来已读取数据的末尾
123     }
124     return -1;
125 }        
126 int main(void)
127 {
128     int listenfd;
129     if((listenfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
130         ERR_EXIT("socket");
131     
132     struct sockaddr_in servaddr;
133     memset(&servaddr,0,sizeof(servaddr));
134     servaddr.sin_family=AF_INET;
135     servaddr.sin_port=htons(5188);
136     servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
137     //套接字选项的设置一定要在bind之前
138     int on=1;
139     if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
140             ERR_EXIT("setsockopt");
141     if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
142         ERR_EXIT("bind");
143     if(listen(listenfd,SOMAXCONN)<0)
144         ERR_EXIT("listen");
145     struct sockaddr_in peeraddr;
146     socklen_t peerlen=sizeof(peeraddr);
147     int conn;
148     pid_t pid;
149     while(1)
150     {
151         if((conn=accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen))<0)
152             ERR_EXIT("accept");
153         printf("ip=%s port=%d
",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
154         pid=fork();
155         if(-1==pid)
156             ERR_EXIT("fork");
157         if(0==pid)//子进程
158         {
159             close(listenfd);
160             do_service(conn);
161             exit(EXIT_SUCCESS);
162         }
163         else close(conn);//父进程
164     }
165     
166     
167     return 0;
168 }    
169         
170 
171 
172 
173         

这是客户端代码:

  1 #include<unistd.h>
  2 #include<sys/types.h>
  3 #include<sys/socket.h>
  4 #include<netinet/in.h>
  5 #include<arpa/inet.h>
  6 #include<stdlib.h>
  7 #include<stdio.h>
  8 #include<errno.h>
  9 #include<string.h>
 10 
 11 #define ERR_EXIT(m) 
 12         do 
 13         { 
 14             perror(m); 
 15             exit(EXIT_FAILURE);
 16         }while(0)
 17             
 18 ssize_t readn(int fd,void *buf,size_t count)
 19 {
 20     size_t nleft=count;
 21     ssize_t nread;
 22     char*bufp=(char*)buf;
 23     while(nleft>0)
 24     {
 25         if((nread=read(fd,bufp,nleft))<0)
 26         {
 27             if(errno==EINTR)
 28                 continue;
 29             return -1;
 30         }
 31         else if(nread==0)
 32             return count-nleft;
 33         bufp+=nread;
 34         nleft-=nread;
 35     }
 36     return count;
 37 }
 38 
 39 ssize_t writen(int fd,const void*buf,size_t count)
 40 {
 41     size_t nleft=count;
 42     ssize_t nwritten;
 43     char*bufp=(char*)buf;
 44     while(nleft>0)
 45     {
 46         if((nwritten=write(fd,bufp,nleft))<0)
 47         {
 48             if(errno==EINTR)
 49                 continue;
 50             return -1;
 51         }
 52         else if(nwritten==0)
 53             continue;
 54         bufp+=nwritten;
 55         nleft-=nwritten;
 56     }
 57     return count;
 58 }
 59 
 60 ssize_t recv_peek(int sockfd,void *buf,size_t len)
 61 {
 62     while(1)
 63     {
 64         int ret=recv(sockfd,buf,len,MSG_PEEK);//窥看,不清除缓存中的数据
 65         if(-1==ret&&EINTR==errno)
 66             continue;
 67         return ret;
 68     }
 69 }
 70 
 71 ssize_t readline(int sockfd,void* buf,size_t maxline)
 72 {
 73     int ret;
 74     int nread;
 75     char* bufp=(char*)buf;
 76     int nleft=maxline;
 77     while(1)
 78     {
 79         ret=recv_peek(sockfd,bufp,nleft);
 80         if(ret<0)
 81             return ret;
 82         else if(0==ret)
 83             return ret;
 84         nread=ret;
 85         int i;
 86         for(i=0;i<nread;i++)//读取到'
'时就应该结束
 87         {
 88             if(bufp[i]=='
')
 89             {
 90                 ret=readn(sockfd,bufp,i+1);
 91                 if(ret!=i+1)
 92                     exit(EXIT_FAILURE);
 93                 return ret;
 94             }
 95         }
 96         //执行到了这儿,说明没有读取到'
'
 97         if(nread>nleft)
 98             exit(EXIT_FAILURE);
 99         nleft-=nread;
100         ret=readn(sockfd,bufp,nread);//清空缓存中的数据,因为之前是窥视,并没有清空缓存
101         if(ret!=nread)
102             exit(EXIT_FAILURE);
103         bufp+=nread;//指针后移,接着去执行while循环,确保数据追加到原来已读取数据的末尾
104     }
105     return -1;
106 }
107 int main(void)
108 {
109     int sock;
110     if((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
111         ERR_EXIT("socket");
112     struct sockaddr_in servaddr;
113     memset(&servaddr,0,sizeof(servaddr));
114     servaddr.sin_family=AF_INET;
115     servaddr.sin_port=htons(5188);
116     servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");//inet_addr将ip地址字符串转为数字形式,且已经是网络字节序    
117     
118     if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)//
119         ERR_EXIT("connect");
120         
121     char sendbuf[1024]={0};
122     char recvbuf[1024]={0};
123     while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL)
124     {
125         writen(sock,sendbuf,strlen(sendbuf));
126         
127         int ret=readline(sock,recvbuf,sizeof(recvbuf));
128         if(-1==ret)
129             ERR_EXIT("readline");
130         else if(0==ret)
131         {
132             printf("client?server close
");
133             break;
134         }
135         fputs(recvbuf,stdout);
136     }
137     
138     return 0;
139 }
手里拿着一把锤子,看什么都像钉子,编程界的锤子应该就是算法了吧!
原文地址:https://www.cnblogs.com/chess/p/4684053.html