第一次dlopen后共享库函数正常工作

时间:2014-05-16 17:37:54

标签: c websocket

我在C中创建一个websocket服务器。由于关闭服务器,重新编译它,再次运行它会对服务器应用程序应该做什么起反作用,我正在寻找动态加载我的函数的方法,这样我就可以保持主服务器应用程序运行,同时能够更改/创建将在其中使用的新功能。我创建了一个函数,允许我通过正确的参数调用一个函数,就像你正常的函数调用一样,但是当我第二次调用它时,第二次它不会做同样的事情,我第二次动态调用它。要按步骤列出我的问题,请考虑以下情况:

情况1

  1. 启动服务器应用程序而不动态调用sendMessage。
  2. 通过浏览器连接到websocket服务器。
  3. 连接成功后,向服务器发送消息(我使用Hello World
  4. 服务器将回显客户端发送的消息。
  5. 再次向服务器发送相同的消息。
  6. 服务器将再次回显消息。 (这是服务器sendMessage函数未动态加载的时候
  7. 重复步骤5和6不会导致客户端断开连接。
  8. 现在使用动态版本的服务器sendMessage函数来回显客户端消息。

    情况2

    1. 启用服务器应用程序,同时允许使用loadFunction调用sendMessage。
    2. 通过浏览器连接到websocket服务器。
    3. 成功连接后,向服务器发送消息(我再次使用Hello World
    4. 服务器将回应客户端发送的消息。
    5. 再次向服务器发送相同的消息。
    6. 这次服务器不回显客户端发送的消息。
    7. 在第一个之后发送更多消息最终会导致连接结束(这是我遇到问题的地方)
    8. 情况1是指我的函数sendMessage被正常调用(而不是通过loadFunction),而情境2是我用加载库的sendMessage调用替换loadFunction的地方保持sendMessage,将其分配给位置函数变量(参见代码)并像通常那样调用函数。

      我认为问题在于sendMessage中的动态加载时的写函数。但是当我不动态加载它时,该功能可以正常工作,这对我来说很奇怪。

      我的问题是为什么我的sendMessage函数与我正常调用它和动态调用它时的运行方式有何不同?以下是两种情况下的一些代码和输出

      sendMessage.c

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <sys/types.h>
      #include <netinet/in.h>
      #include <unistd.h>
      #include "include/structs.h"
      //#include "include/functions.h"
      
      /*
       * sendMessage: this function is used then we want to send message (s)
       *              of length (len) from the server to a client (sock)
       *
       * ARGUMENTS
       * sock:    the socket where we want the message to go
       * s:       A string containing the message we want to send
       * len: the length of the string s
       */
      void *sendMessage(int sock, char *s, int len) {
          int frameCount;
          uint16_t len16;
          char frame[10];
          char *reply =   malloc(sizeof(char) * (len + 8));
      
          frame[0] =  '\x81';
      
          if (len <= 125) {
              frame[1] = len;
              frameCount = 2;
          } else if (len >= 126 && len <= 65535) {
              frame[1] = 126;
              len16 = htons(len);
              memcpy(frame + 2, &len16, 2);
              frameCount = 4;
          } else {
              frame[1] = 127;
              //NOTE: HAVE NOT FULLY CONFIGURED A MESSAGE OF THIS LENGTH (TODO)
              //frame[2] = (char)( ((char)len >> 56) & (char)255 );
              //frame[3] = (char)( ((char)len >> 48) & (char)255 );
              //frame[4] = (char)( ((char)len >> 40) & (char)255 );
              //frame[5] = (char)( ((char)len >> 32) & (char)255 );
              //frame[6] = (char)( ((char)len >> 24) & (char)255 );
              frame[7] = (char)( ((char)len >> 16) & (char)255 );
              frame[8] = (char)( ((char)len >> 8) & (char)255 );
              frame[9] = (char)( ((char)len) & (char)255 );
              frameCount = 10;
          }//END IF
      
          memcpy(reply, frame, frameCount);
          memcpy(reply + frameCount, s, len);
      
          //printf("sock: %d\ts: %s\tlen: %d\n", sock, s, len);
          if (write(sock, reply, strlen(reply)) <= 0) {
              printf("\n\nWE ARE NOT WRITING!!\n\n");
          } else {
              //printf("we did write\n");
          }//END IF
      
          free(reply);
          reply = NULL;
      
          return NULL;
      }//END FUNCTION
      

      loadFunction.c

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <dlfcn.h>
      #include "include/functions.h"
      
      int checkForError(char *error) {
          if (error != NULL) {
              printf("ERROR: %s\n", error);
              exit(EXIT_FAILURE);
          }//END IF
      
          return 0;
      }//END IF
      
      void * loadFunction(char *func, void ** args) {
          void *handle;
          //void * (*alterStruct)(int sock, char *action);
      
          int filenameLength;
          char * filename;
          //void *(*funcPtr);
      
      
          filenameLength = strlen("lib/lib") + strlen(func) + strlen(".dll");
          filename = malloc(sizeof(char) * (filenameLength + 1));
          strcpy(filename, "lib/lib");
          strcat(filename, func);
          strcat(filename, ".dll");
      
          handle = dlopen(filename, RTLD_LAZY);
          free(filename);
          if (!handle) {
              checkForError(dlerror());
          }//END IF
      
          dlerror();
      
          if (strncmp(func, "sendMessage", strlen(func)) == 0) {
              void * (*funcPtr)(int, char *, int);
              //*(void **) (&funcPtr) = dlsym(handle, func);
              funcPtr = (void *)dlsym(handle, func);
              checkForError(dlerror());
              (*funcPtr)((int)args[0], (char *)args[1], (int)args[2]);
              //free(*(void **)(&funcPtr));
              //*(void **) (&funcPtr) = NULL;
          }// else if (strncmp(func, "alterStruct", strlen(func)) == 0) {
              //void * (*funcPtr)(int sock, char *action);
          //} else if (strncmp(func, "execute", strlen(func)) == 0) {
              //void * (*funcPtr)(const char *command, clientStruct s, FILE **in, FILE **out, FILE **err);
          //} else {
              //void * (*funcPtr)(int sock, char *s, int len);
          //}//END IF
      
          dlclose(handle);
          handle = NULL;
          return NULL;
      
      
          return NULL;
      
      }//END loadFunction
      

      如果您需要更多代码来解决此问题,我可以在GitHub here上访问它(动态分支是可以找到问题的地方)

      另外,我正在使用Cygwins&#39; GNU gcc编译器(我从来没有遇到过编译问题)编译我的应用程序和库,因此我可能无法访问某些Linux命令(例如dlmopen)。也就是说,请不要说使用不同的编译器,因为到目前为止我没有其他问题,我不打算改变编译代码的方式。

      我没有记录用于编译loadFunction中使用的libsendMessage.dll的命令。您可以使用以下

      获取它
      gcc -c -fPIC sendMessage.c
      mv sendMessage.o objects/sendMessage.o
      gcc -shared -o lib/libsendMessage.dll objects/sendMessage.o libfunctions.c
      

      提前谢谢。

1 个答案:

答案 0 :(得分:0)

我已经发现了我的问题,这是一个奇怪的问题。

当我使用我的动态方法i sendMessage测试printf函数时,确切地说是通过套接字发送了什么,显然它正在发送正确的消息。但是,它也发送了loadFunction函数中我的文件名变量中的字符,对我来说奇怪的是,当sendMessage malloc编辑,释放和设置时,内存地址可以访问sendMessage在调用动态dlopen(filename, RTLD_LAZY)的函数之前调用NULL方式。

<强>解决方案

memset(filename, '\0', filenameLength)打开我的库后,我使用{{1}}将空字符写入filename的内存地址,使字符串包含所有空字符,当作为字符访问时计为字符串的结尾

我不确定为什么我必须在这个应用程序中使用memset,但我知道它已多次修复了我的字符串错误。