socket编程(5)
本章主要内容
1.read\write和recv\send函数的区别
2.readline实现
3.用readline实现回射客户/服务器
4.getsockname、getpeername的简单介绍
5.gethostname、gethostbyname、gethostbyaddr的简单介绍
一、
1.recv函数仅仅只能用于套接字io,不能用于文件io和其他IO,而read函数
可以用于所有IO.
2.多了一个flag选项
MSG_OOB 紧急指针或带外数据 TCP头部中有一个紧急指针 指定发送紧
急数据
MSG_PEEK 接收缓冲区的数据,但是并不将数据从缓冲区清除(read函数一
旦数据从缓冲区读走,就将数据清除)
接下来我们封装一份recv_peek函数。
1 ssize_t recv_peek(int sockfd, void *buf, size_t len) 2 { 3 while(1) 4 { 5 int ret = recv(sockfd,buf,len,MSG_PEEK); 6 if(ret = -1&& errno == EINTR) 7 continue; 8 return ret; 9 } 10 }
接下来,我们用封装的recv_peek函数来实现readline功能。
readline指向按行读取 \n.所以readline函数也可以解决粘包问题。(有些
协议在包为加上\r\n)ftp协议就是这么做的。
1.一个字节一个字节读直到遇到\n这种方法需要多次读,消耗cpu
2.加上静态变量,则不能重入
3.这里用偷窥(MSG_PEEK)
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]; int n; 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"); break; } fputs(recvbuf,stdout); writen(conn,recvbuf,strlen(recvbuf)); } }
总结:
这一章,
1.我们主要封装了一个readline函数,使用recv函数来实现,并且使用到了
一个选项 MSG_PEEK来实现偷窥缓冲区中数据;
2.将readline函数用到了回射服务器当中;
3.介绍了如何获取本机的主机名,获取本机的ip地址等函数
附加:服务器端完整代码:
1 #include<stdio.h> 2 #include <sys/socket.h> 3 #include<sys/types.h> 4 #include<unistd.h> 5 6 #include <netinet/in.h> 7 #include <arpa/inet.h> 8 9 #include<stdlib.h> 10 #include<errno.h> 11 #include<string.h> 12 13 #define ERR_EXIT(m)\ 14 do\ 15 { \ 16 perror(m);\ 17 exit(EXIT_FAILURE);\ 18 }while(0) 19 20 21 ssize_t readn(int fd, void *buf, size_t count) 22 { 23 size_t nleft = count; 24 ssize_t nread; 25 char *bufp = (char*)buf; 26 27 while(nleft > 0) 28 { 29 if((nread = read(fd,bufp,nleft)) <0 ) 30 { 31 if (errno == EINTR) /*被信号中断*/ 32 continue; 33 return -1; 34 } 35 else if(nread ==0) 36 return count - nleft;/*对方关闭*、*/ 37 bufp += nread; 38 nleft -= nread; 39 } 40 return count; 41 42 } 43 44 ssize_t writen(int fd, const void *buf, size_t count) 45 { 46 size_t nleft = count; 47 ssize_t nwriten; 48 char *bufp = (char*)buf; 49 50 while(nleft > 0) 51 { 52 if((nwriten = write(fd,bufp,nleft)) <0 ) 53 { 54 if (errno == EINTR) /*被信号中断*/ 55 continue; 56 return -1; 57 } 58 else if(nwriten ==0) 59 continue ;/*什么也没有发生过*/ 60 bufp += nwriten; 61 nleft -= nwriten; 62 } 63 return count; 64 } 65 66 ssize_t recv_peek(int sockfd, void *buf, size_t len) 67 { 68 while(1) 69 { 70 int ret = recv(sockfd,buf,len,MSG_PEEK); 71 if(ret == -1&& errno == EINTR) 72 continue; 73 return ret; 74 } 75 } 76 77 ssize_t readline(int sockfd, void *buf, size_t maxline) 78 { 79 int ret; 80 int nread; 81 char *bufp = buf; 82 int nleft = maxline; 83 while(1) 84 { 85 ret = recv_peek(sockfd,bufp,nleft); 86 if(ret<0) 87 return ret; 88 else if(ret ==0) 89 return ret; 90 nread = ret; 91 int i; 92 for(i=0;i<nread;i++) 93 { 94 if(bufp[i]=='\n') 95 { 96 ret = readn(sockfd,bufp,i+1); 97 if(ret != i+1) 98 exit(EXIT_FAILURE); 99 return ret; 100 } 101 } 102 if(nread > nleft) 103 exit(EXIT_FAILURE); 104 nleft -= nread; 105 ret = readn(sockfd,bufp,nread); 106 if(ret != nread) 107 exit(EXIT_FAILURE); 108 bufp += nread; 109 } 110 return -1; 111 } 112 113 void do_service(int conn) 114 { 115 116 /*处理通信的细节*/ 117 char recvbuf[1024]; 118 int n; 119 while(1) 120 { 121 memset(recvbuf,0,sizeof(recvbuf)); 122 int ret = readline(conn,recvbuf,1024); 123 if(ret ==-1) 124 ERR_EXIT("readline"); 125 if(ret == 0) 126 { 127 printf("client close"); 128 break; 129 } 130 fputs(recvbuf,stdout); 131 writen(conn,recvbuf,strlen(recvbuf)); 132 133 } 134 } 135 int main() 136 { 137 /*create a socket*/ 138 int listenfd; 139 if((listenfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) 140 printf("socket err"); 141 142 /*init addr*/ 143 struct sockaddr_in servaddr; 144 memset(&servaddr,0,sizeof(servaddr)); 145 servaddr.sin_family = AF_INET; 146 servaddr.sin_port = htons(5188); 147 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 148 /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/ 149 /*inet_aton("127.0.0.1",&servaddr.sin_addr);*/ 150 151 int on = 1;/*开启地址重复利用*/ 152 if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0) 153 printf("setsockopt err"); 154 155 /*将创建的套接字与本地地址进行绑定*/ 156 if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0) 157 printf("blind err"); 158 159 /*监听套接字*/ 160 if (listen(listenfd,SOMAXCONN) < 0 ) 161 printf("listen err:"); 162 163 164 struct sockaddr_in peeraddr; 165 socklen_t peerlen = sizeof(peeraddr); 166 int conn; 167 168 pid_t pid; 169 while(1) 170 { 171 172 /*主动套接字*/ 173 if ((conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen)) < 0 ) 174 printf("accept err"); 175 176 printf("ip = %s,port = %d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port)); 177 178 pid = fork(); 179 if(pid == -1) 180 printf("fork err"); 181 else if(pid == 0) 182 { 183 close(listenfd); 184 do_service(conn); 185 exit(EXIT_SUCCESS); 186 } 187 else 188 { 189 close(conn); 190 } 191 192 } 193 194 return 0; 195 196 }
客户端完整代码:
1 #include<stdio.h> 2 #include <sys/socket.h> 3 #include<sys/types.h> 4 #include<unistd.h> 5 6 #include <netinet/in.h> 7 #include <arpa/inet.h> 8 9 #include<stdlib.h> 10 #include<errno.h> 11 #include<string.h> 12 13 #define ERR_EXIT(m) \ 14 do\ 15 { \ 16 perror(m);\ 17 exit(EXIT_FAILURE);\ 18 }while(0) 19 20 21 22 ssize_t readn(int fd, void *buf, size_t count) 23 { 24 size_t nleft = count; 25 ssize_t nread; 26 char *bufp = (char*)buf; 27 28 while(nleft > 0) 29 { 30 if((nread = read(fd,bufp,nleft)) <0 ) 31 { 32 if (errno == EINTR) /*被信号中断*/ 33 continue; 34 return -1; 35 } 36 else if(nread ==0) 37 return count - nleft;/*对方关闭*、*/ 38 bufp += nread; 39 nleft -= nread; 40 } 41 return count; 42 43 } 44 45 ssize_t writen(int fd, const void *buf, size_t count) 46 { 47 size_t nleft = count; 48 ssize_t nwriten; 49 char *bufp = (char*)buf; 50 51 while(nleft > 0) 52 { 53 if((nwriten = write(fd,bufp,nleft)) <0 ) 54 { 55 if (errno == EINTR) /*被信号中断*/ 56 continue; 57 return -1; 58 } 59 else if(nwriten ==0) 60 continue ;/*什么也没有发生过*/ 61 bufp += nwriten; 62 nleft -= nwriten; 63 } 64 return count; 65 } 66 67 ssize_t recv_peek(int sockfd, void *buf, size_t len) 68 { 69 while(1) 70 { 71 int ret = recv(sockfd,buf,len,MSG_PEEK); 72 if(ret == -1&& errno == EINTR) 73 continue; 74 return ret; 75 } 76 } 77 78 ssize_t readline(int sockfd, void *buf, size_t maxline) 79 { 80 int ret; 81 int nread; 82 char *bufp = buf; 83 int nleft = maxline; 84 while(1) 85 { 86 ret = recv_peek(sockfd,bufp,nleft); 87 if(ret<0) 88 return ret; 89 else if(ret ==0) 90 return ret; 91 nread = ret; 92 int i; 93 for(i=0;i<nread;i++) 94 { 95 if(bufp[i]=='\n') 96 { 97 ret = readn(sockfd,bufp,i+1); 98 if(ret != i+1) 99 exit(EXIT_FAILURE); 100 return ret; 101 } 102 } 103 if(nread > nleft) 104 exit(EXIT_FAILURE); 105 nleft -= nread; 106 ret = readn(sockfd,bufp,nread); 107 if(ret != nread) 108 exit(EXIT_FAILURE); 109 bufp += nread; 110 } 111 return -1; 112 } 113 int main() 114 { 115 /*创建套接字*/ 116 int sockfd; 117 if((sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) 118 printf("socket err"); 119 120 /*serv addr and port*/ 121 struct sockaddr_in servaddr; 122 memset(&servaddr,0,sizeof(servaddr)); 123 servaddr.sin_family = AF_INET; 124 servaddr.sin_port = htons(5188); 125 servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 126 127 /*连接,一旦连接成功,sockfd套接字就处于已连接状态,从逻辑上 128 是与服务器端的conn套接字连接*/ 129 if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0) 130 printf("connect err"); 131 struct sockaddr_in localaddr; 132 socklen_t addrlen = sizeof(localaddr); 133 if(getsockname(sockfd,(struct sockaddr*)&localaddr,&addrlen)<0) 134 ERR_EXIT("getsockname"); 135 printf("ip=%s port=%d\n",inet_ntoa(localaddr.sin_addr),ntohs(localaddr.sin_port)); 136 137 char sendbuf[1024]={0}; 138 char recvbuf[1024]={0}; 139 while(fgets(sendbuf,sizeof(sendbuf),stdin) != NULL) 140 { 141 writen(sockfd,sendbuf,strlen(sendbuf)); 142 int ret = readline(sockfd,recvbuf,sizeof(recvbuf)); 143 if(ret == 0) 144 { 145 printf("client close"); 146 break; 147 } 148 149 fputs(recvbuf,stdout); 150 memset(sendbuf,0,sizeof(sendbuf)); 151 memset(recvbuf,0,sizeof(recvbuf)); 152 153 } 154 155 close(sockfd); 156 157 return 0; 158 159 }