Socket再學習——開發板ds18b20獲取溫度值並上報服務器

經過一段時間的學習、積累,自己對於物聯網、計算機網絡、socket編程等相關的領域和知識有了新的理解,網絡部分一直是重中之重。因此重新再學習了Linux下的socket,並結合了一個簡單的實際例子再來學習client端與server端工作流程。

===========================================

文件描述符:實際上就是Linux內核給分配的“稱謂”,而在我們的TCP通訊中,會有多個文件描述符需要處理。
比如:
listenfd:監聽描述符
connectfd:請求連接描述符
accept:接受連接描述符
read/write/recv/send…:IO描述符

服務器建立連接的流程和涉及到的函數:socket()、bind()、listen()、accept()、connect()、close()。

結構體struct sockaddr_in :網絡通訊五元組,本端IP,本端端口、對端IP、對端端口、協議類型。

這裏寫圖片描述
參考這張圖便能瞭解client和server之間之間是如何進行交互的了。

===========================================
int socket(int domain, int type, int protocol);
向內核申請一個套接字,設置該套接字協議類型。

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
爲套接字綁定IP和端口

int listen(int sockfd, int backlog);
以socket套接字和該套接字綁定的IP信息listen在內核開啓監聽,並返回監聽描述符。這裏代碼是不阻塞的,但是內核一直在監聽對應的端口。

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
連接請求,此時代碼是阻塞的。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
接受連接的請求,代碼阻塞。就是從listen中取描述符。

int close(int fd);
關閉描述符。

這裏寫圖片描述
是不是就是類似我們熟悉的三次握手呀。

Client請求時間是不確定的,當多個請求到Server時,處於請求隊列,等待listen的端口逐個處理至就緒隊列。
connect處於阻塞態等待請求從listen的就緒隊列被accept調度返回具體用於數據傳輸的accept_fd描述符。
accept處於阻塞態,當請求隊列爲空或處理完畢時。
所以,三次握手由connet發起,accept結束,途中經歷listen的隊列維護。

下面貼出代碼,功能是由在開發板上的DS18B20採集了溫度數據,上報到服務器,並且顯示當前的時間。當然,前提是開發板已經使能了DS18B20的驅動,否則是跑不起應用程序的。
DS18B20的驅動編寫可以參考這裏:http://blog.csdn.net/u010944778/article/details/48058433

temp_server.c:

/*********************************************************************************
 *      Copyright:  (C) 2017 TangBin<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  temp_server.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(06/18/2017)
 *         Author:  TangBin <[email protected]>
 *      ChangeLog:  1, Release initial version on "06/18/2017 08:22:56 PM"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <time.h>

#define MAXLINE 1024
#define PORT 8200
#define BUFFSIZE 2048

int main (int argc, char **argv)
{
    int listenfd, connfd;
    int n;
    char buf[BUFFSIZE];
    struct sockaddr_in servaddr;

    if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("create socket error:");
        exit(0);
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(PORT);

    if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
    {
        perror("bind error:");
        exit(0);
    }

    if(listen(listenfd, 10) < 0)
    {
        perror("listen error:");
        exit(0);
    }

    printf("waiting client's request... ...\n");
    while(1)
    {
        memset(&buf, 0, sizeof(buf));
        time_t rawtime;
        struct tm* ptime;
        char time_arr[100];
        time(&rawtime);
        ptime = localtime(&rawtime);
        strftime(time_arr, 20, "%y-%m-%d %I:%M:%S", ptime);

        if((connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) < 0)
        {
            perror("accept error:");
            continue;
        }
        n = recv(connfd, buf, MAXLINE, 0);
        strcat(buf, "℃\0");
        printf("temperature :%s\n", buf);
        printf("%s\n",time_arr);
    }
    close(connfd);

    return 0;
} /* ----- End of main() ----- */

temp_client.c:

/*********************************************************************************
 *      Copyright:  (C) 2017 TangBin<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  temp_client.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(06/19/2017)
 *         Author:  TangBin <[email protected]>
 *      ChangeLog:  1, Release initial version on "06/19/2017 12:52:25 PM"
 *                 
 ********************************************************************************/

#include <sys/types.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define MAXLINE 1024
#define PORT 8200
float get_temp(void)
{
    int fd;
    int data = 0;
    float temperature = 0;
    unsigned char buff[2];

    if((fd=open("/dev/ds18b20",O_RDWR | O_NDELAY | O_NOCTTY)) < 0)
    {
        perror("open device ds18b20 failed.");
        exit(1);
    }

    printf("open ds18b20 success.");

    read(fd, buff, sizeof(buff));
    data = ((int)buff[1]) << 8;  //高8位移動到16BITS的高8位
    data |= (int)buff[0];   //合併低8位到溫度讀值
    temperature = ((float)data)*0.0625;  
    /*DS18B20的溫度操作是使用16位,也就是說分辨率是0.0625;要求出正數的十進制值,必須將讀取到的LSB字節,MSB字節進行整合處理,然後乘以0.0625即可*/

    close(fd);
    printf("emperature is %4f \r\n",temperature);

    return temperature;
}

int main (int argc, char **argv)
{
    float res;
    int socketfd;
    char temp[50];
    char s_line[MAXLINE];
    struct sockaddr_in servaddr;

    if(2 != argc)
    {
        printf("please input: ./client <ipaddr>\n");
        return 0;
    }

    if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("create socket error: %s(errno: %d).\n", strerror(errno), errno);
        return 0;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);

    if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)   //點分十進制ip轉換爲點分二進制ip
    {
        printf("inet_pton error for %s.\n", argv[1]);
        return 0;
    }
    if(connect(socketfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
    {
        printf("connect error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    res = get_temp();
    gcvt(res ,4 ,temp);    //浮點型數轉換爲字符串,參數1:被轉換的值;參數2:存儲的有效數字位數;參數3:結果的存儲位置。
    memset(s_line ,0, sizeof(s_line));
    strcpy(s_line, temp);

    if(send(socketfd, s_line, strlen(s_line), 0) < 0)   //將數據發送到服務器端
    {
        printf("send error:%s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    close(socketfd);

    return 0;
} /* ----- End of main() ----- */

測試結果,用的是實驗室的服務器和自己的開發板fl2440:
這裏寫圖片描述
(其中connect error是因爲在服務器上我的server程序沒運行)
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章