实现的功能:一次只能读取一行,客户端输入之后,一回车,马上字符串传到服务器端并显示在终端,然后服务器端将字符串又传回给客户端。
服务器端可以接收多个客户端的连接请求,并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\n"); 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++)//读取到‘\n‘时就应该结束 106 { 107 if(bufp[i]==‘\n‘) 108 { 109 ret=readn(sockfd,bufp,i+1); 110 if(ret!=i+1) 111 exit(EXIT_FAILURE); 112 return ret; 113 } 114 } 115 //执行到了这儿,说明没有读取到‘\n‘ 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\n",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++)//读取到‘\n‘时就应该结束 87 { 88 if(bufp[i]==‘\n‘) 89 { 90 ret=readn(sockfd,bufp,i+1); 91 if(ret!=i+1) 92 exit(EXIT_FAILURE); 93 return ret; 94 } 95 } 96 //执行到了这儿,说明没有读取到‘\n‘ 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\n"); 133 break; 134 } 135 fputs(recvbuf,stdout); 136 } 137 138 return 0; 139 }
时间: 2024-10-12 13:12:43