客户端代码实现

1、了解TCP/IP协议

TCP/IP协议是由很多很多协议组成的集合,不仅仅是tcp和ip协议,把这个集合统称为TCP/IP协议族。TCP/IP按层次分为四层:应用层、传输层、网络层和数据链路层。通过下图可以了解TCP/IP协议结构:
在这里插入图片描述

2、了解socket网络编程

socket网络编程其实不难实现,只要你了解了客户端与服务器端之间是如何实现连接、发送和接收数据,知道如何调用相应的函数以及函数的用法就可以实现,以下面的流程图为例:
在这里插入图片描述
只要搞明白图中的这几个函数,便可以实现简单的socket通信。温度实时监控项目就可以在实现通信的基础上完善其所需的功能即可。这只是简单的步骤,要想彻底搞明白socket网络编程,建议了解计算机网络及其通信原理、OSI(Open System Interconnection,开放系统互联)七层网络模型、tcp/ip协议及对比了解学习TCP/IP 四层参考模型和 OSI 七层参考模型,这对今后的网络编程特别重要。下面是客户端用到的几个函数介绍:
(1)socket()函数原型:int socket(int protofamily, int type, int protocol);
第一个参数是协议族,常用的协议族有,AF_INET(IPV4)、AF_INET6(IPV6)、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
第二个参数是用来指定socket的类型,常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
第三个参数就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。注意的是并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。
Socket的功能用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。所以它的返回值为int类型。
(2)connect()函数原型:int connect(int sockfd, struct sockaddr serv_addr, int addrlen);
它的三个参数:sockfd 是系统调用 socket() 返回的套接字文件描述符。serv_addr 是 保存着目的地端口和 IP 地址的数据结构 struct sockaddr。addrlen 设置 为 sizeof(struct sockaddr)。调用失败后返回-1.
(3)write()函数原型:ssize_t write(int fd,const void
buf,size_t count);
参数说明:
fd:是文件描述符(write所对应的是写,即就是1)
buf:通常是一个字符串,需要写入的字符串
count:是每次写入的字节数
调用成功返回写入的字节数,失败返回-1并设置errno.
(4)read()函数原型:ssize_t read(int fd,void*buf,size_t count)
参数说明:
fd: 是文件描述符
buf: 为读出数据的缓冲区;
count: 为每次读取的字节数(是请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读写位置向后移)
调用成功后返回读入的字节数,失败返回-1并设置errno。
(5)close()函数是用来等读写完成后,调用它关闭文件描述符即可。

3、编程实现客户端

这是我的客户端初始化代码:

153                 /*socket初始化*/
154                 if( (sockfd=socket(AF_INET,SOCK_STREAM,0))<0 )
155                 {
156                         printf("create socket failure:%s\n",strerror(errno));
157                         goto cleanup;
158                 }
159                 printf("create socket[%d] successfully!\n",sockfd);
160 
161                 memset(&servaddr,0,sizeof(servaddr));
162                 servaddr.sin_family = AF_INET;
163                 servaddr.sin_port = htons(SERVER_PORT);
164                 inet_aton( SERVER_IP, &servaddr.sin_addr );
165 
166                 if( connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))<0 )
167                 {
168                         printf("connect to server [%s:%d] failure: %s\n", SERVER_IP, SERVER_PORT, strerror(errno));
169                         goto cleanup;
170                 }
171 
172                 if( write(sockfd, buf, strlen(buf)) < 0 )
173                 {
174                         printf("Write data to server [%s:%d] failure: %s\n", SERVER_IP, SERVER_PORT, strerror(errno));
175                         goto cleanup;
176                 }
177 cleanup:
178                 close(sockfd);

当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个
具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数。调用connect()函数之前要将端口和ip地址从主机字节序转为网络字节序,绑定了端口和地址后,调用connet()函数连接服务器,连接成功以后,客户端就调用write()函数发送数据给服务器,发送完后关闭socket的套接字。当然客户端连接服务器的时候,不可能只连接一次就不连了,所以当某个函数调用出错或者连接不成功,我们要重新连接直到发送数据成功,加一个while循环,就解决了。也可以调用signal()函数安装信号实现循环。当我们调用函数的时候,最好加个判断。

4、项目功能完善

实现socket通信之后,就来对功能完善一下,这个项目要求我们读取DS18B20上面的温度,以字符串“ID/时间/温度”形式上报采样温度,.首先我们获取温度,获取时间,时间的获取Linux下也有相应的函数,然后用snprintf函数将它们连接成指定的字符串格式。

 /*获取时间*/
147                 time(&timep);
148                 p=localtime(&timep);
/*将数据连接成字符串*/
149                 snprintf(buf,sizeof(buf),"%s/%d-%d-%d %d:%d:%d/%f",name,(1900 + p->tm_year),(1 + p->tm_mon),p->tm_mday,
150                                 (p->tm_hour+12),p->tm_min,p->tm_sec,temp);
151                 printf("%s\n",buf);

从ds18b20上获取温度:

 35 /*获取温度函数*/
 36 int get_temperature(float *temp)
 37 {
 38         char w1_path[50] = "/sys/bus/w1/devices/";
 39         char chip[20];
 40         char buf[128];
 41         DIR *dirp;
 42         struct dirent *direntp;
 43         int fd =-1;
 44         char *ptr;
 45         float value;
 46         int found = 0;
 47 
 48         if( !temp )
 49         {
 50                 return -1;
 51         }
 52         if((dirp = opendir(w1_path)) == NULL)
 53         {
 54                 printf("opendir error: %s\n", strerror(errno));
 55                 return -2;
 56         }
 57         while((direntp = readdir(dirp)) != NULL)
 58         {
 59                 if(strstr(direntp->d_name,"28-"))
 60                 {
 61                         strcpy(chip,direntp->d_name);
 62                         found = 1;
 63                 }
 64         }
 65         closedir(dirp);
 66         if( !found )
 67         {
 68                 printf("Can not find ds18b20 in %s\n", w1_path);
 69                 return -3;
 70         }
  71         strncat(w1_path, chip, sizeof(w1_path));
 72         strncat(w1_path, "/w1_slave", sizeof(w1_path));
 73         if( (fd=open(w1_path, O_RDONLY)) < 0 )
 74         {
 75                 printf("open %s error: %s\n", w1_path, strerror(errno));
 76                 return -4;
 77         }
 78 
 79         if(read(fd, buf, sizeof(buf)) < 0)
 80         {
 81                 printf("read %s error: %s\n", w1_path, strerror(errno));
 82                 return -5;
 83         }
 84 
 85         ptr = strstr(buf, "t=")+2;
 86         if( !ptr )
 87         {
 88                 printf("ERROR: Can not get temperature\n");
 89                 return -6;
 90         }
 91         *temp = atof(ptr)/1000;
 92 
 93         return 0;
 94 }
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值