1 /* 2 此程序是tcp/ip通信服务器端程序,测试运行在redhat5上 3 重构readline函数,解决粘包问题——利用“\n”识别一个消息边界 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]==‘\n‘) 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\n",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.\n"); 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 }
时间: 2024-10-14 13:27:12