我有这个简单的客户端/服务器套接字代码,用于发送和接收lambda函数。问题出现在recvlambda()函数内的客户端上,当我在收到它后尝试调用lambda时,我得到了seg fault @
printf(“Hello World!%i,%i \ n”,x,y);
调试显示无法访问x和y,它们的内存地址不好。
我在Ubuntu 13.10上使用gcc 4.8.1。
我将x,y传递给sendlambda()函数中的lambda。这不应该是错误的。知道为什么吗?
#include <iostream>
#include <time.h>
#include <gmpxx.h>
using namespace std;
typedef int (*func)();
/* Server code in C */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <functional>
void sendlambda(int ConnectFD)
{
int x = 2342342;
int y = 23454234;
function<int (void)> f = [x, y]() mutable -> int
{
printf("Hello World! %i, %i\n", x, y);
};
printf("sending lambda of %i bytes\n", sizeof(f));
write(ConnectFD, (void*)&f, sizeof(f));
}
void recvlambda(int SocketFD)
{
char buffer[1024];
read(SocketFD, (void*)buffer, sizeof(buffer));
function<int (void)> &f = *(function<int (void)> *)buffer;
f();
}
int server()
{
printf("server\n");
struct sockaddr_in stSockAddr;
int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(-1 == SocketFD)
{
printf("can not create socket\n");
exit(EXIT_FAILURE);
}
memset(&stSockAddr, 0, sizeof(stSockAddr));
stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons(1100);
stSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(-1 == bind(SocketFD,(struct sockaddr *)&stSockAddr, sizeof(stSockAddr)))
{
printf("error bind failed\n");
close(SocketFD);
exit(EXIT_FAILURE);
}
if(-1 == listen(SocketFD, 10))
{
printf("error listen failed\n");
close(SocketFD);
exit(EXIT_FAILURE);
}
for(;;)
{
int ConnectFD = accept(SocketFD, NULL, NULL);
if(0 > ConnectFD)
{
printf("error accept failed\n");
close(SocketFD);
exit(EXIT_FAILURE);
}
/* perform read write operations ...*/
sendlambda(ConnectFD);
if (-1 == shutdown(ConnectFD, SHUT_RDWR))
{
printf("can not shutdown socket\n");
close(ConnectFD);
close(SocketFD);
exit(EXIT_FAILURE);
}
close(ConnectFD);
}
close(SocketFD);
return EXIT_SUCCESS;
}
int client()
{
printf("client\n");
struct sockaddr_in stSockAddr;
int Res;
int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (-1 == SocketFD)
{
printf("cannot create socket\n");
exit(EXIT_FAILURE);
}
memset(&stSockAddr, 0, sizeof(stSockAddr));
stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons(1100);
Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr);
if (0 > Res)
{
printf("error: first parameter is not a valid address family\n");
close(SocketFD);
exit(EXIT_FAILURE);
}
else if (0 == Res)
{
printf("char string (second parameter does not contain valid ipaddress\n)");
close(SocketFD);
exit(EXIT_FAILURE);
}
if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr)))
{
printf("connect failed\n");
close(SocketFD);
exit(EXIT_FAILURE);
}
/* perform read write operations ... */
recvlambda(SocketFD);
(void) shutdown(SocketFD, SHUT_RDWR);
close(SocketFD);
return EXIT_SUCCESS;
}
int main(int argc, char** argv)
{
if(argc>1 && strcmp(argv[1], "server")==0)
server();
else
client();
return 0;
}
答案 0 :(得分:3)
一般情况下,您不能指望复制类型的按位副本(例如std::function
)能够正常工作。
这是可能发生的事情:在服务器进程中创建的std::function
对象是一个函数指针,指向用于初始化它的lambda,它位于服务器进程的内存中。当客户端进程收到它时,它会尝试调用此函数指针,该指针指向不同进程的地址空间(服务器进程的指针)。这种段错误并不奇怪。
但是你仍然处于未定义的行为区域,因为你分配了一个字符数组,然后尝试访问它,好像它是一个std::function
对象。
答案 1 :(得分:0)
lambda只是另一个分配在sendlambda
麻袋上的本地人。 &f
函数与获取该函数中声明的任何其他本地的地址没有什么不同。在这种情况下,虽然f
不是一种可以简单地按值复制并在另一帧中重新水合的类型(它不是POD类型)。这就是您在recvlambda
答案 2 :(得分:0)
我尝试了更多的东西并得出结论,我想要做的就是序列化一个lambda。 Serialize C++ functor
这是不可能的。
当客户端代码在某处(在其自己的进程空间中)使用或定义仿函数,然后接收一些字节流,这些字节流被转换为同一个仿函数时,代码可以工作。它以某种方式调用客户端中的该函数。
但是如果客户端进程没有仿函数块,并且您尝试将字节流强制转换为仿函数,则它不起作用,您无法调用该仿函数。
如果它的闭包变得更糟,服务器上的副本捕获的变量无法映射到客户端上。服务器进程的数据段中的变量,它们在服务器的仿函数中被引用。当该仿函数被发送到客户端时,客户端会尝试在给定数据段的内存地址的情况下访问这些变量。但这些地址是针对服务器进程的。所以我得到了读取访问权限。
似乎我正在尝试使用lambdas进行代码注入,但这是不可能的。