tcp/ip通信第5期之服务器端程序

  1 /*
  2 此程序是tcp/ip通信服务器端程序,测试运行在redhat5上
  3 重构readline函数,解决粘包问题——利用“
”识别一个消息边界
  4 */
  5 
  6 #include<stdio.h>
  7 #include<netinet/in.h>
  8 #include<arpa/inet.h>
  9 #include<unistd.h>
 10 #include<fcntl.h>
 11 #include<sys/types.h>
 12 #include<sys/stat.h>
 13 #include<sys/socket.h>
 14 #include<stdio.h>
 15 #include<stdlib.h>
 16 #include<string.h>
 17 #include<signal.h>
 18 #include<errno.h>
 19 
 20 ssize_t readn(int fd, void *buf, size_t count)
 21 {
 22     size_t nleft = count;//还留下多少字节没有读
 23     ssize_t nread; //已经读了多少字节
 24     char *bufp = (char *)buf;
 25     while (nleft > 0)
 26             {
 27                 if ((nread = read(fd, bufp, nleft)) < 0)
 28                 {
 29                     if (errno == EINTR)
 30  //被信号中断,errno这个全局变量的值就会等于EINTR。
 31                         continue;
 32                     return -1;
 33                 }
 34                 else if (nread == 0) //对方关闭或者已经读到eof
 35                     return count - nleft;
 36                 bufp += nread;
 37                 nleft -= nread;
 38             }
 39             return count;
 40 }
 41      
 42 ssize_t writen(int fd, const void * buf, size_t count) 
 43 {
 44         size_t nleft = count;
 45         ssize_t nwritten;
 46         char *bufp = (char *)buf;
 47         while (nleft > 0)
 48             {
 49                 if ((nwritten = write(fd, bufp, nleft)) < 0)
 50                 {
 51                     if (errno == EINTR)
 52                         continue;
 53  //要保证读取的字节数为指定字节数,所以继续
 54                     return -1;
 55                 }
 56                 else if (nwritten == 0)        
 57                     continue;
 58  //由于其他原因引起的什么都没有写进,则继续操作,保证指定字节数
 59                 bufp += nwritten;
 60                 nleft -= nwritten;
 61             }
 62             return count;
 63 }
 64 
 65 ssize_t recv_peek(int sockfd,void *buf,size_t len)
 66 {
 67     while(1)
 68     {
 69         int ret=recv(sockfd,buf,len,MSG_PEEK);
 70         if(ret==-1&&errno==EINTR)
 71             continue;
 72         return ret;
 73     }
 74 }
 75 
 76 ssize_t recv_line(int sockfd,void *buf,size_t len)
 77 {
 78     int ret;//记录函数返回值
 79     int nread;//已经读到的字节数
 80     char *bufp=buf;
 81     int nleft=len;
 82     while(1)
 83     {
 84         ret=recv_peek(sockfd,bufp,nleft);
 85         if(ret<0)
 86             return ret;
 87         else if(ret==0)
 88             return ret;
 89         nread=ret;
 90         int i;
 91         for(i=0;i<nread;i++)
 92         {
 93             if(bufp[i]=='
')
 94             {
 95                 ret=readn(sockfd,bufp,i+1);
 96                 if(ret!=i+1)
 97                     exit(1);
 98                 return ret;
 99             }
100         }
101         if(nread>nleft)
102             exit(1);
103         nleft -= nread;
104         ret=readn(sockfd,bufp,nread);
105         if(ret!=nread)
106             exit(0);
107         bufp+=nread;
108     }
109     return -1;
110 }
111 
112 
113 #define port 5188
114 int main()
115 {
116     int listenfd;
117     //*****创建套接字*******
118     if((listenfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
119     /*if((listenfd=socket(PF_INET,SOCK_STREAM,0))<0)*/
120         perror("error");
121         
122     //*******ipv4地址结构**********
123     struct sockaddr_in servaddr;
124     memset(&servaddr,0,sizeof(servaddr)); //清空结构体变量
125     servaddr.sin_family=AF_INET;
126     servaddr.sin_port=htons(port); //使用端口号:5188
127     servaddr.sin_addr.s_addr=htonl(INADDR_ANY);//INADDR_ANY表示使用本机的任意可用ip地址,转换成网络地址序
128     /*servaddr.sin_addr.s_addr = inet_addr("127.168.0.12");*/
129     /*inet_aton("127.168.0.12",&servaddr.sin_addr);*/
130     
131     //*******绑定套接字和本机地址***********
132     //1、设置REUSEADDR选项
133     int N=1;
134     if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&N,sizeof(N))<0)
135         perror("error");
136     //2、进行绑定
137     if(bind(listenfd,(struct sockaddr*)(&servaddr),sizeof(struct sockaddr))<0)
138         perror("error");
139     
140     //********将绑定的套接字转换为监听状态********
141     if(listen(listenfd,SOMAXCONN)<0) //SOMAXCONN这个宏表示最大队列值
142         perror("error");
143     /*一旦调用listen函数,那么这个套接字就变成了被动套接字(只能被动接受连接——accept,
144       不能发起连接——connect),否则还是主动套接字(可以发起连接——connect)*/
145     
146     //********接收对方的连接请求**************
147     struct sockaddr_in peeraddr;  //定义对方地址
148     socklen_t peerlen=sizeof(peeraddr);
149     int con;
150     if((con=accept(listenfd,(struct sockaddr*)(&peeraddr),&peerlen))<0)
151         perror("error");
152     else
153         printf("client_ip=%s,client_port=%d
",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
154     
155     //*******数据通信过程***********
156     char recvbuf[1024];
157     while(1)
158     {
159         memset(recvbuf,0,sizeof(recvbuf));
160         int ret=recv_line(con,recvbuf,sizeof(recvbuf));//con这里是已连接套接字,不再是被动套接字,而是主动套接字了
161         if(ret==-1)
162             exit(1);
163         if(ret==0)
164         {
165             printf("client_port is closed.
");
166             break;
167         }
168         fputs(recvbuf,stdout);
169         writen(con,recvbuf,strlen(recvbuf));
170         memset(recvbuf,0,sizeof(recvbuf));
171     }
172     close(con);
173     close(listenfd);
174     return 0;
175     
176 }
内在的趣味,表面的繁琐
原文地址:https://www.cnblogs.com/data1213/p/4815858.html