BSD套接字不工作
Posted
技术标签:
【中文标题】BSD套接字不工作【英文标题】:BSD socket not working 【发布时间】:2012-01-28 06:03:28 【问题描述】:更新:根据建议更新了 src。 strcat a \n\0 到命令并更改 sizeof。客户端在发送时“成功”,但服务器在接收时从不“成功”。
我付出了很多努力来尝试让一个常规的旧 TCP 套接字正常工作。我基本上有一个简单的客户端和服务器设置。目前我的服务器上什么都没有收到,我的客户端上只有 4 个字节(本例中为“GOTY”)。
服务器输出输出这个并继续:
RECEIVED (null)
客户端输出只是阻塞并且永不停止。如果我杀死服务器,我会得到这个输出
Connect success
Received Response:
服务器
int Server::update(char *getbuf)
int ready = poll(pollfds, 1, 100);
if (ready == -1)
logger.info("Poll failed");
return FALSE;
if (pollfds[0].revents & POLLIN)
ssize_t z;
logger.info("Connection available");
struct sockaddr_in client_address;
memset(&client_address, 0, sizeof client_address);
socklen_t alen = sizeof(client_address);
// ACCEPT
clientsocket = accept(serversocket, (struct sockaddr *)&client_address, &alen);
if(!clientsocket)
logger.fail("Accept Fail");
// READ
z = recv(clientsocket, getbuf, 512, MSG_WAITALL);
if (z < 0)
fprintf(stderr,"receive failure\n");
else
fprintf(stderr,"receive succeed\n");
getbuf[z] = 0;
respond(getbuf);
//closeConnection();
logger.data("RECEIVED %s", getbuf);
getbuf[z] = 0;
return TRUE;
return FALSE;
void Server::respond(char *response)
// SEND
ssize_t z;
z = send(clientsocket, response, strlen(response), 0);
if (z < 0)
fprintf(stderr,"send failure\n");
else
fprintf(stderr,"send succeed\n");
客户
我添加了客户端代码。我现在使用 strlen 但它似乎没有帮助。
int main(int argc, char **argv)
int abort = 0;
int port;
in_addr_t address;
char *command;
ssize_t z;
int com_socket;
struct sockaddr_in server_address;
struct timeval timeout;
int opt;
int val = 0;
char getbuf[512];
//Defaults
address = inet_addr("127.0.0.1");
port = 4949;
//INCLUDED ARGUMENTS FROM CLI
while((opt = getopt(argc, argv, "a:p:c:")) > 0)
switch(opt)
case 'a':
address = inet_addr(optarg);
break;
case 'p':
port = atoi(optarg);
break;
case 'c':
abort = 1;
command = (char *)optarg;
break;
default:
fprintf(stderr, "-a IPADDRESS -p PORT -c COMMAND\n",argv[0]);
// Server
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port);
server_address.sin_addr.s_addr = address;
if (server_address.sin_addr.s_addr == INADDR_NONE)
fprintf(stderr, "Server address failed\n");
if(!abort)
fprintf(stderr, "No Command given\n");
fprintf(stderr, "-a IPADDRESS -p PORT -C COMMAND\n");
exit(0);
else
fprintf(stderr, "Address %s Port %d Command %s\n", inet_ntoa(server_address.sin_addr), port, command);
// Create com_socket
com_socket = socket(PF_INET, SOCK_STREAM, 0);
if (com_socket == -1)
fprintf(stderr, "Socket failed\n");
/*
// Client
struct sockaddr_in client_address;
memset(&client_address,0,sizeof client_address);
client_address.sin_family = AF_INET;
client_address.sin_port = 0;
client_address.sin_addr.s_addr = ntohl(INADDR_ANY);
if (client_address.sin_addr.s_addr == INADDR_NONE)
fprintf(stderr, "Client address failed\n");
// Bind
z= bind(com_socket, (struct sockaddr *)&client_address, sizeof (client_address));
if ( z == -1 )
fprintf(stderr,"Binding port\n");
*/
timeout.tv_sec = 2; /* 2 seconds */
timeout.tv_usec = 0; /* + 0 usec */
socklen_t addrlen = sizeof(struct sockaddr_in);
// Connect
//z = connectWithTimeout(com_socket, (struct sockaddr *) &server_address, len_inet, &timeout);
z = connect(com_socket, (struct sockaddr *) &server_address, sizeof(server_address));
if(z == -1)
if(errno == EINPROGRESS)
fprintf(stderr, "EINPROGRESS non block start\n");
if(errno == EALREADY)
fprintf(stderr, "EALREADY non block subsequent request\n");
fprintf(stderr, "Connect failed\n");
else
fprintf(stderr, "Connect success\n");
strcat(command, "\n\0");
// SEND
z = send(com_socket, command, strlen(command)+2, 0);
if (z < 0)
fprintf(stderr,"send failure\n");
else
fprintf(stderr,"send succeed\n");
// READ
z = recv(com_socket, getbuf, 512, MSG_WAITALL);
if (z < 0)
fprintf(stderr,"receive failure\n");
else
fprintf(stderr,"receive succeed\n");
// Output
fprintf(stderr, "Received Response: %s\n", getbuf);
close(com_socket);
exit(1);
我只是不知道为什么这不起作用。我一次又一次地经历过。
这是一个嵌入式 Linux 系统。
【问题讨论】:
您没有显示实际调用send()
的函数。我建议在strace
下运行您的服务器和客户端,以查看它实际传递给send()
的数据和长度。
您是否首先尝试使用众多工具(特别是 gcc -Wall -g
、gdb
、valgrind
、strace
...)使您的代码在常规 Linux 桌面上运行?有货吗?
【参考方案1】:
在服务器中:这是完全错误的。 strlen() 返回缓冲区中字符串的长度在 read() /recv() 之前。
// READ
z = recv(clientsocket, getbuf, strlen(getbuf), MSG_WAITALL);
if (z < 0)
fprintf(stderr,"receive failure\n");
另外:在 response() 中,strlen() 假定响应“字符串”是空终止的。不是。
z = send(clientsocket, response, strlen(response), 0);
客户端代码中似乎存在相同类型的错误。
更新:由于 OP 不知道如何在没有字符串函数的情况下生活,我将说明如何做到这一点。
... snipped ...
// READ
z = recv(clientsocket, getbuf, 512, MSG_WAITALL);
if (z < 0)
// .... if (errno == EAGAIN) continue;
fprintf(stderr,"receive failure %d(%s) \n"
, errno, strerror(errno) );
break;
fprintf(stderr,"receive succeed\n");
respond(getbuf, z);
return TRUE;
return FALSE;
void respond(char *response, size_t siz)
// SEND
size_t done;
ssize_t z;
for (done = 0; done < siz; done += z)
z = send(clientsocket, response+done, siz-done, 0);
if (z < 0)
if (errno == EAGAIN) continue;
fprintf(stderr,"send failure %d(%s)\n"
, errno, strerror(errno) );
break;
fprintf(stderr,"send succeed\n");
【讨论】:
那么我应该如何知道我收到的内容的长度?如果我输入缓冲区的大小,它会一直挂到它收到 256 个字节吗? 没有。它将读取当时可用的尽可能多的字节。您的程序应该记住它已经收集了多少数据,以及缓冲区中还剩下多少空间。 所以你确认我应该把缓冲区的大小放在这种情况下?因为它看起来好像仍然挂在接收端,值为 256(缓冲区大小)。 视情况而定。如果你有消息边界(例如命令行:以'\n'结尾的命令),阅读器应该积累数据直到它收集到一个完整的“命令”)如果它只是一个回显服务器(它似乎是) ,服务器可以在收到字节后回显它们。 (但它必须跟踪收到的金额,并且在回显时应该使用确切的金额。FWIW:即使 write() / send() 也可以发送部分数据:您应该检查它的返回值和采取相应的行动。 嗯,它是一个网络服务器,目前只是回显它接收到的内容以用于调试目的。如果我错了,请纠正我,但 TCP 数据包很容易适合我发送 【参考方案2】:由于你实际上并没有分享你的“响应”功能的来源,我的精神力量告诉我它看起来像这样:
void Server::respond(char* str)
send(clientsock, str, sizeof(str), 0);
因此,“sizeof(str)”的计算结果为 4,因为这是您机器上指针的大小。这与客户端接收“GOTY”(您打算发送的消息的前 4 个字节)一致。
将其更改为以下内容:
void Server::respond(char* str)
send(clientsock, str, strlen(str), 0);
此外,您不应假设 send 和 recv 将返回一个等于您预期发送或接收的数据量的值。因此,您应该循环对 send/recv 的非阻塞调用,直到整个消息被发送或使用。
【讨论】:
如何知道消息何时完成? 你没有。您必须将传入的字符保存在缓冲区中并等到出现“\n”。 (或其他一些协议定义的语句终止符)【参考方案3】:套接字上的发送操作通常会被缓冲,因此您的响应函数可能没有刷新所有数据。在respond()
的末尾尝试刷新操作以获取额外的数据。
在我看来,您正在看到阻塞行为,您可能想了解阻塞/非阻塞如何影响套接字。
【讨论】:
套接字没有缓冲;没有flush功能。 啊。我只使用过套接字库(如 python asyncore、luasocket 等),所以我猜他们添加了自己的缓冲区。我一直认为它是由操作系统完成的。以上是关于BSD套接字不工作的主要内容,如果未能解决你的问题,请参考以下文章
CFNetwork 在 iOS 和 MacOS 上优于 BSD 套接字和 GCD 的优势?