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 }

 

posted @ 2017-03-24 19:33  ren_zhg1992  阅读(131)  评论(0)    收藏  举报