分类 C 下的文章

在之前的文章中,实现了一个从客户端发送字符串数据到服务器的Socket程序。但是在实际应用中,往往需要通过Socket传输其他复杂一点的数据,例如struct结构或者uint8_t数组等等。

服务端(Linux版)

#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX 80
#define PORT 2087
#define SA struct sockaddr

// Function designed for chat between client and server.
void func(int sockfd)
{
    char buff[MAX];
    for(;;)
    {
        bzero(buff, sizeof(buff));
        recv(sockfd, buff, sizeof(buff), 0);
        if ((strncmp(buff, "exit", 4)) == 0) {
            printf("Client Exit...\n");
            break;
        }        
        printf("From Client : %s", buff);
        send(sockfd, buff, sizeof(buff), 0);
    }
}

static void sig_handler(int sig){
    
    int retval;
    
    if ( sig == SIGCHLD ){ 
        // 等待子程序的結束狀態
        wait(&retval);
        
        printf("CATCH SIGNAL PID=%d\n",getpid());
    }
}
  
// Driver function
int main(void)
{
    int sockfd, connfd;
    struct sockaddr_in servaddr, cli;
    socklen_t len;
    pid_t pid;
  
    // socket create and verification
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        printf("socket creation failed...\n");
        exit(0);
    }
    else
        printf("Socket successfully created..\n");
    
    bzero(&servaddr, sizeof(servaddr));
  
    // assign IP, PORT
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(PORT);
    
    int reuse = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(int)) == -1)
        printf("Can't set the reuse option on the socket");
  
    // Binding newly created socket to given IP and verification
    if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0) {
        printf("socket bind failed...\n");
        exit(0);
    }
    else
        printf("Socket successfully binded..\n");
  
    // Now server is ready to listen and verification
    if ((listen(sockfd, 5)) != 0) {
        printf("Listen failed...\n");
        exit(0);
    }
    else
        printf("Server listening..\n");
        
  
    while(1)
    {
        // Accept the data packet from client and verification
        len = sizeof(cli);        
        connfd = accept(sockfd, (SA*)&cli, &len);
        if (connfd < 0) {
            printf("server acccept failed...\n");
            exit(0);
        }
        else
            printf("server acccept the client...\n");
        
        // 呼叫 signal 來接收 SIGCHLD 信號
        signal(SIGCHLD,sig_handler);
        
        pid = fork();
        
        if(pid < 0)
            printf("ERROR on fork");
        
        if(pid == 0)
        {
            close(sockfd);
            
            // Function for chatting between client and server
            func(connfd);
            close(connfd);
            exit(0);            
        }
        else
        {
            close(connfd);
        }
            
    }    
  
    // After chatting close the socket
    close(sockfd);
    
    return 0;
}

网络中的进程如何通信

在计算机中,本地进程通信(IPC)有很多种方法,可以总结为4类:

静态库

简单来说静态库(static library)就是将一些函数和变量打包,可以直接在其他地方调用。与之对应的还有动态库(dynamic library)。两者相比:

#include <stdio.h>

int main(void)
{
    char s[] = "How big is it?";
    char* t = s;
    printf("数组有多大:%lld\n", sizeof(s));
    printf("指针有多大:%lld\n", sizeof(t));
    return 0;
}

在之前的文章中提到了使用libserialport库通过串口与传感器通信。通过C语言的strtoull()函数:将字符串转换成unsigned long long(无符号长整型数)将01 03 00 00 00 01 84 0A字符串转换成无符号长整型数

之前使用了libmodbus这个库来与传感器通讯,使用ModBus485协议。但是有几个传感器在最后的CRC校验时出错,这情况在使用Pythonminimalmodbus库时也出现过,所以当时使用了pyserial库,现在C语言的libmodbus库也出现这个问题,只能说明这几个传感器有问题。

一直想要在C语言的程序中输出变量的类型,但是没有类似PHP中的var_dump函数,通过搜索得知可以使用gdb程序来调试C程序,gdbLinux平台上常用的调试工具了,搭配gcc使用。

指针的概念

要知道指针的概念,要先了解变量在内存中如何存储的。在存储时,内存被分为一块一块的。每一块都有一个特有的编号。而这个编号可以暂时理解为指针,就像酒店的门牌号一样。

写在前面

之前的项目上使用了Pythonminimalmodbus库,但是我感觉如果从站多了之后,性能会有所下降,所以一直想使用C语言来编写与传感器通信的程序,看看是否比Python更高效