1 /*此程序是tcp/ip通信的客户机端程序, 2 测试运行在redhat6系统上 3 重构readline函数,解决粘包问题——利用“ ”识别一个消息边界 4 */ 5 #include<stdio.h> 6 #include<stdlib.h> 7 #include<unistd.h> 8 #include<sys/types.h> 9 #include<sys/socket.h> 10 #include<arpa/inet.h> 11 #include<netinet/in.h> 12 #include<string.h> 13 #include<signal.h> 14 #include<errno.h> 15 16 #define port 5188 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 //被信号中断,errno这个全局变量的值就会等于EINTR。 29 continue; 30 return -1; 31 } 32 else if (nread == 0) //对方关闭或者已经读到eof 33 return count - nleft; 34 bufp += nread; 35 nleft -= nread; 36 } 37 return count; 38 } 39 40 ssize_t writen(int fd, const void * buf, size_t count) 41 { 42 size_t nleft = count; 43 ssize_t nwritten; 44 char *bufp = (char *)buf; 45 while (nleft > 0) 46 { 47 if ((nwritten = write(fd, bufp, nleft)) < 0) 48 { 49 if (errno == EINTR) 50 continue; 51 //要保证读取的字节数为指定字节数,所以继续 52 return -1; 53 } 54 else if (nwritten == 0) 55 continue; 56 //由于其他原因引起的什么都没有写进,则继续操作,保证指定字节数 57 bufp += nwritten; 58 nleft -= nwritten; 59 } 60 return count; 61 } 62 63 ssize_t recv_peek(int sockfd,void *buf,size_t len) 64 { 65 while(1) 66 { 67 int ret=recv(sockfd,buf,len,MSG_PEEK); 68 if(ret==-1&&errno==EINTR) 69 continue; 70 return ret; 71 } 72 } 73 74 ssize_t recv_line(int sockfd,void *buf,size_t len) 75 { 76 int ret;//记录函数返回值 77 int nread;//已经读到的字节数 78 char *bufp=buf; 79 int nleft=len; 80 while(1) 81 { 82 ret=recv_peek(sockfd,bufp,nleft); 83 if(ret<0) 84 return ret; 85 else if(ret==0) 86 return ret; 87 nread=ret; 88 int i; 89 for(i=0;i<nread;i++) 90 { 91 if(bufp[i]==' ') 92 { 93 ret=readn(sockfd,bufp,i+1); 94 if(ret!=i+1) 95 exit(1); 96 return ret; 97 } 98 } 99 if(nread>nleft) 100 exit(1); 101 nleft -= nread; 102 ret=readn(sockfd,bufp,nread); 103 if(ret!=nread) 104 exit(0); 105 bufp+=nread; 106 } 107 return -1; 108 } 109 110 int main() 111 { 112 int sock; 113 //*****创建套接字******* 114 if((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0) 115 /*if((listenfd=socket(PF_INET,SOCK_STREAM,0))<0)*/ 116 perror("error"); 117 118 //*******ipv4地址结构********** 119 struct sockaddr_in servaddr; 120 memset(&servaddr,0,sizeof(servaddr)); //清空结构体变量 121 servaddr.sin_family=AF_INET; 122 servaddr.sin_port= htons(port); //使用端口号:5188 123 servaddr.sin_addr.s_addr=inet_addr("192.168.248.129"); //ip地址使用对方的地址,即服务器地址 124 printf("the port_id:%d ",port); 125 126 //*********连接请求************ 127 if(connect(sock,(struct sockaddr*)(&servaddr),sizeof(servaddr))<0) //使用对方的ip地址 128 perror("error"); 129 else 130 printf("connected success. "); 131 132 //************通信过程************* 133 char sendbuf[1024]={0}; 134 char recvbuf[1024]={0}; 135 while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL) 136 { 137 writen(sock,sendbuf,strlen(sendbuf)); 138 int ret=recv_line(sock,recvbuf,sizeof(recvbuf)); 139 if(ret==-1) 140 perror("error "); 141 if(ret==0) 142 { 143 printf("peer is closed. "); 144 break; 145 } 146 147 fputs(recvbuf,stdout); 148 memset(sendbuf,0,sizeof(sendbuf)); //清空缓存 149 memset(recvbuf,0,sizeof(recvbuf)); 150 } 151 close(sock); 152 return 0; 153 }