计时器触发新线程时的 tcp 通信问题

tcp communication issue when new thread is triggered by timer

这是我与机器人和 s7 plc 通信的巨大代码的一部分,两者并行工作。问题是,在我之前虽然问题是 plc 通信的一部分,但现在我已经将我的所有代码减少到一个似乎也失败的客户端-服务器应用程序。 该代码基本上以下一种方式工作;客户端每 50 毫秒向服务器发送一条 "hello" 消息。当发送 20 条消息时,将启动 7 秒的计时器。当计时器被触发时,第二个线程启动,这个线程更改变量 f=1 并完成。同时,主程序将打印 hello,但是当此变量 f 更改为 1 时,它应该只打印 "aaaaa",但它从未发生过。

对于PLC,问题是分段错误。我的想法是 TCP 通信失败(为什么?)并且 PLC 阻止访问。我在移动机器人手臂之前尝试了相同的程序,并使用 wireshark 进行分析,您可以看到 TCP 是如何一直发送的,但机器人又一次停止了。 最后,通过这个客户端应用程序,可以看出它们没有按预期工作。

希望大家能帮帮我。

非常感谢;)

客户

#include <stdlib.h>
#include <arpa/inet.h> 
#include <stdio.h>
#include <string.h>
#include <sys/types.h>  // Primitive System Data Types 
#include <errno.h>      // Errors */
#include <sys/wait.h>   // Wait for Process Termination 
#include "plc_interface.h"
#include <netinet/in.h>
#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/socket.h>

 timer_t firstTimerID;
 timer_t secondTimerID;
    struct itimerspec it;
    int t_block3=5;
 pthread_t thread_robot;
 pthread_attr_t thread_attr;
 int res, z;
 int f=0;
    


//THREAD FUNCTION ONCE THE REQUIRED BLOCK IS DETECTED
void *detection_robot(){
 printf("First\n");
 //f==1 to send the message aaaaaa
 f=1;
 pthread_exit(NULL);

}


int setTimer(timer_t * timerID, int time) {
  struct itimerspec its;
  //Interval for starting again
  its.it_interval.tv_sec = 0;
  its.it_interval.tv_nsec = 0;
  //Timer time
  its.it_value.tv_sec = time;
  its.it_value.tv_nsec = 0;
  //Arm/disarmer a per process time
  timer_settime (*timerID, 0, &its, NULL);

  return 0;
}

//TIMER INTERRUPTION
static void timerHandler (int sig, siginfo_t * si, void *uc_) {
  timer_t *tidp;
  int iret;
  tidp = si->si_value.sival_ptr;

  //Initializes the attributes for the thread
  res= pthread_attr_init(&thread_attr);
  res=pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED);

    if (*tidp == firstTimerID) {
   printf ("First timer\n");

   //New thread to detect the second detection sensor
   iret = pthread_create( &thread_robot, &thread_attr, detection_robot, NULL);
   if(iret)
    {
    fprintf(stderr,"Error - pthread_create() return code: %d\n",iret);
    exit(EXIT_FAILURE);
    }
    } else if (*tidp == secondTimerID) {
   printf ("Second timer\n");
    }

    z--;

}


//ARM THE TIMERS
static int makeTimer (timer_t * timerID) {
    struct sigevent te;
    struct sigaction sa;
    int sigNo = SIGRTMIN;
    // Set up signal handler.
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = timerHandler;       //Action when singal is triggered
    sigemptyset (&sa.sa_mask);
    if (sigaction (sigNo, &sa, NULL) == -1) {
   perror ("sigaction");
    }
    // Set and enable alarm
    te.sigev_notify = SIGEV_SIGNAL;       //Gnerate alarm upon expiration
    te.sigev_signo = sigNo;       //SIGALRM
    te.sigev_value.sival_ptr = timerID;   //Timer ID
    //Create a per_process timer using the timer ID
    timer_create (CLOCK_REALTIME, &te, timerID);
  return 0;
}


int main(int argc , char *argv[])
{
    int sock;
    struct sockaddr_in server;
    char message[6]={'h','e','l','l','o','[=10=]'};
       char message2[6]={'a','a','a','a','a','[=10=]'};
    char server_reply[2000];
 //Initialize the timers
   makeTimer(&firstTimerID);
   makeTimer(&secondTimerID);

    //Create socket
    sock = socket(AF_INET , SOCK_STREAM , 0);
    if (sock == -1)
    {
        printf("Could not create socket");
    }
    puts("Socket created");
     
    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    server.sin_family = AF_INET;
    server.sin_port = htons( 8888 );
 
    //Connect to remote server
    if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0)
    {
        perror("connect failed. Error");
        return 1;
    }
    puts("Connected\n");
    //keep communicating with server
 int i=0;
 while(1){
       //Send hello every 50ms
        if( send(sock , message , 6 , 0) < 0)
        {
            puts("Send failed");
            return 1;
        }
   i++;
   if (i==20){
      setTimer (&firstTimerID, t_block3);
      i=0;
     }
     
   //f-> 1, send aaaaaa when timer is triggered, but here is when it get stacked
   if (f==1) {
    f=0;
    if( send(sock , message2 , 6 , 0) < 0)
    {
     puts("Send failed");
     return 1;
    }}

   usleep(500000); //500ms
  }

}

服务器

 
#include<stdio.h>
#include<string.h>    //strlen
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr
#include<unistd.h>    //write
 
int main(int argc , char *argv[])
{
    int socket_desc , client_sock , c , read_size;
    struct sockaddr_in server , client;
    char client_message[2000];
     
    //Create socket
    socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    if (socket_desc == -1)
    {
        printf("Could not create socket");
    }
    puts("Socket created");
     
    //Prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons( 8888 );
     
    //Bind
    if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
    {
        //print the error message
        perror("bind failed. Error");
        return 1;
    }
    puts("bind done");
     
    //Listen
    listen(socket_desc , 3);
     
    //Accept and incoming connection
    puts("Waiting for incoming connections...");
    c = sizeof(struct sockaddr_in);
     
    //accept connection from an incoming client
    client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
    if (client_sock < 0)
    {
        perror("accept failed");
        return 1;
    }
    puts("Connection accepted");
     
     while(1){
    //Receive a message from client
    recv(client_sock , client_message , 2000 , 0);
    printf("%s\n", client_message);
     
 }

}

问题是赋值:

f = 1;

对其他线程不可见,因此 main() 线程永远看不到更改。要更正,您需要使用同步机制,例如 pthread_mutex_t, which must be acquired when reading or writing f, or atomic_int 如果您的编译器支持 C11。

已解决,评论中有解释:)

代码已解决,只需按照 hmjd 的建议添加互斥量即可。谢谢 ;)

我不清楚这是互斥锁的问题,因为在我使用的其他代码中,没有共享变量会导致 code/communication 中断。我将在这里展示我的 plc 代码,以展示我正在使用的内容。我删除了与机器人的额外 tcp 通信,我只是在这里展示 PLC 内存的连续读取,它一直非常稳定,但在触发简单计时器时会中断(并非总是如此)。如果为机器人更改 PLC,这只是正常的 tcp 通信发送移动命令,仍然会发生相同的情况。

#include <stdlib.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include "nodavesimple.h"
#include "nodave.h"
#include "openSocket.h"
#include "plc_interface.h"
#include <sys/types.h>  // Primitive System Data Types
#include <errno.h>      // Errors */
#include <sys/wait.h>   // Wait for Process Termination
#include "plc_interface.h"
#include <netinet/in.h>
#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <semaphore.h>
#ifdef LINUX
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#define UNIX_STYLE
#endif

#ifdef BCCWIN
#include <time.h>
    void usage(void);
#define WIN_STYLE

#endif


#ifdef PLAY_WITH_KEEPALIVE
#include <sys/socket.h>
#endif



 timer_t firstTimerID;
    struct itimerspec it;
    int t_block3=3;
 pthread_t thread_robot;
 pthread_attr_t thread_attr;
 int robot_detection=0;




//THREAD FUNCTION ONCE THE REQUIRED BLOCK IS DETECTED
void *detection_robot(){
 printf("First\n");
 pthread_exit(0);

}




//TIMER INTERRUPTION
static void timerHandler (int sig, siginfo_t * si, void *uc_) {
  timer_t *tidp;
  int iret;
  tidp = si->si_value.sival_ptr;

  //Initializes the attributes for the thread
  pthread_attr_init(&thread_attr);
  pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED);

    if (*tidp == firstTimerID) {
   printf ("First timer\n");

   //New thread to detect the second detection sensor
   iret = pthread_create( &thread_robot, &thread_attr, detection_robot, NULL);
   if(iret)
    {
    fprintf(stderr,"Error - pthread_create() return code: %d\n",iret);
    exit(EXIT_FAILURE);
    }
    }


}


//ARM THE TIMERS
static int makeTimer (timer_t * timerID) {
    struct sigevent te;
    struct sigaction sa;
    int sigNo = SIGRTMIN;
    // Set up signal handler.
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = timerHandler;       //Action when singal is triggered
    sigemptyset (&sa.sa_mask);
    if (sigaction (sigNo, &sa, NULL) == -1) {
   perror ("sigaction");
    }
    // Set and enable alarm
    te.sigev_notify = SIGEV_SIGNAL;       //Gnerate alarm upon expiration
    te.sigev_signo = sigNo;       //SIGALRM
    te.sigev_value.sival_ptr = timerID;   //Timer ID
    //Create a per_process timer using the timer ID
    timer_create (CLOCK_REALTIME, &te, timerID);
  return 0;
}





int main(int argc, char **argv) {
    int adrPos, useProtocol, useSlot;
    int res,barcode_number, r, t;
    int c=0, a=0;
    //Locate the robot in the initial position
    //robot_init();

 //Initialize the timer
   makeTimer(&firstTimerID);


#ifdef PLAY_WITH_KEEPALIVE
    int opt;
#endif

    daveSetDebug(daveDebugPrintErrors);
    adrPos=1;
    useProtocol=daveProtoISOTCP;
    useSlot=2;
    daveInterface * di;
    daveConnection * dc;
    _daveOSserialType fds;
    PDU p;
    daveResultSet rs;

    //If the input doesn't have enough parameters, printf the usage
    if (argc<2) {
  usage();
  exit(-1);
    }



 //If there is a problem in the communication it takes 20 seconds to stablish communication again
    fds.rfd=openSocket(102, argv[adrPos]);

#ifdef PLAY_WITH_KEEPALIVE
    errno=0;
    opt=1;
    res=setsockopt(fds.rfd, SOL_SOCKET, SO_KEEPALIVE, &opt, 4);
    LOG3("setsockopt %s %d\n", strerror(errno),res);
#endif
    fds.wfd=fds.rfd;

    if (fds.rfd>0) {
 di =daveNewInterface(fds,"IF1",0, useProtocol, daveSpeed187k);
 daveSetTimeout(di,5000000);
 dc =daveNewConnection(di,2,0,useSlot);  // insert your rack and slot here
 if (0==daveConnectPLC(dc)) {
     printf("Connected.\n");

   //START THE BELT
         a=0;//a=1 to start the belt
         printf("Start the belt \n");
         res=daveWriteBits(dc,  daveDB, 111, 4, 1, &a);
         res=daveWriteBits(dc,  daveDB, 111, 3, 1, &a);
         printf("function result:%d=%s\n", res, daveStrerror(res));

    //Remove the memory flag of the detection sensor again
    res=daveWriteBits(dc,  daveFlags, 0, 504, 1, &c);
    res=daveWriteBits(dc,  daveFlags, 0, 520, 1, &c);

    usleep(2000000);
    printf("Trying to read the sensor until block is detected\n");
    //SENSOR DETECTION
    while(1){

     //Read request  --> Stable until the timer is triggered
     davePrepareReadRequest(dc,&p);
     daveAddVarToReadRequest(&p, daveFlags, 0, 63, 1);
     daveAddVarToReadRequest(&p, daveFlags, 0, 65, 1);
     daveAddVarToReadRequest(&p, daveDB, 4, 103, 1);
     res=daveExecReadRequest(dc, &p, &rs);
     if (res==1){ printf("Error during the request\n");}

     //First result --> detection sensor after the barcode
     res=daveUseResult(dc, &rs, 0);
     if (res==1){ printf("Error reading the first result\n");}
        t=daveGetU8(dc); //T = 1 if detection in the barcode place
        //Second result --> detection sensor before the robot position
     res=daveUseResult(dc, &rs, 1);
     if (res==1){ printf("Error reading the detection sensor before the robot\n");}
     r=daveGetU8(dc); //R = 1 if detection in the robot place
     //Third result --> read the number stored
     daveUseResult(dc, &rs, 2);
     barcode_number=daveGetU8(dc);
     //Free the memory
     daveFreeResults(&rs);

     if (r==1){
      //Remove the memory flag referred to the second sensor
      res=daveWriteBits(dc,  daveFlags, 0, 520, 1, &c);
      if (res==1){ printf("Error removing the flag of the second detector\n");}
     }

     //Wen detection..
     if (t==1){
       t=0;
       //Remove the memory flag of the detection
       res=daveWriteBits(dc,  daveFlags, 0, 504, 1, &c);
       if (res==1){ printf("Error removing the flag of the sensor closed to the robot\n");}


       //If number three is detected the belt will stop in 3 seconds respectively
       switch(barcode_number){

       case 3: printf("Block number three, timer will be triggered in 3 seconds\n");
         //Start the timer
         setTimer (&firstTimerID, t_block3);
         break;

       case 2: printf("Block number two, belt won't stop\n");
         break;

       case 4: printf("Block number four, belt won't stop\n");
         break;

       default:printf("Wrong lecture\n");

       }}


     usleep(50000); //Rest until next sampling
  }
 //Close socket
 closeSocket(fds.rfd);
 printf("Finished.\n");

 return 0;
 } else {//daveconnected
     printf("Couldn't connect to PLC.\n \n");
     closeSocket(fds.rfd);
     return -2;}

    }//fds.rds > 0
     else {
 printf("Couldn't open TCP port. \nPlease make sure a CP is connected and the IP address is ok. \n");
     return -1;
    }
}