所以我在我的程序中遇到了一个奇怪的问题,它基本上是 C 中的客户端/服务器套接字编程示例。我已经花时间解决这个问题,但我似乎无法找到导致该问题的原因问题。
如果需要,我可以发布更多详细信息,但以下内容不起作用。
while(1)
{
char word[256];
gets(word);
printf("%s",word);
if(strcmp(word, "TRANSLATE") == 0 || strcmp(word, "GET") == 0 ||strcmp(word, "STORE") == 0 || strcmp(word, "EXIT") == 0)
{
if(strcmp(word, "TRANSLATE") == 0)
{
if(send(sock , word , strlen(word) , 0)<0)
{
printf("Send Error\n");
return 1;
}
bzero(server_reply,2000);
if(recv(sock , server_reply , 2000 , 0)<0)
{
puts("Recieving Error\n");
}
//Print out 200ok message
printf("%s\n", server_reply);
//Get the first word
gets(word2);
while(strcmp(word2,"." )!= 0)
{
if(send(sock , word2 , strlen(word2) , 0)<0)
{
printf("Send Error\n");
return 1;
}
gets(word2);
}
if(recv(sock , server_reply , 2000 , 0)<0)
{
puts("Recieving Error\n");
}
printf("%s\n", server_reply);
//STILL NEED TO WORK ON PARSING OUT SPACES BECAUSE RESULT IS IN A STRING, JUST WITH SPACES.
//ALSO NEED TO DO SERVER SIDE CAPITALIZATION
}
if(strcmp(word, "GET") == 0)
{
printf("Getting");
/*
if(send(sock , word , strlen(word) , 0)<0)
{
printf("Send Error\n");
return 1;
}
bzero(server_reply,2000);
if(recv(sock , server_reply , 2000 , 0)<0)
{
puts("Recieving Error\n");
}
//Print out 200ok message
printf("%s\n", server_reply);
/*
if(recv(sock , server_reply , 2000 , 0)<0)
{
puts("Recieving Error\n");
}
//Print out the KANSAS message
printf("%s\n", server_reply);
*/
}
}
一旦我在“GET”选择中取消注释,代码就会中断,它甚至不会命中 if 语句之前的 printf() 语句。如果我将其省略,它会完美地命中所有正确的语句。我错过了什么吗? 另外,我认为这可能与服务器通信有关,所以这是服务器代码。
#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( 5149 );
//Bind
if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
{
//print the error message
printf("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)
{
printf("accept failed");
return 1;
}
puts("Connection accepted");
//Receive a message from client
while( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 )
{
if(strcmp(client_message,"TRANSLATE")==0)
{
*(client_message + read_size) = '\0';
char word[256]="200 OK TRANSLATE";
write(client_sock, word, strlen(word));
/*
while( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 )
{
*(client_message + read_size) = '\0';
puts(client_message);
write(client_sock,client_message,strlen(client_message));
if(strcmp(client_message,".")==0)
{
printf("breaking");
break;
}
printf("IN while loop");
}
printf("Out of while loop");
*/
}
if(strcmp(client_message,"GET")==0)
{
*(client_message + read_size) = '\0';
char word[256]="200 OK GET";
write(client_sock, word , strlen(word));
char word2[256]="I don't think we're in Kansas anymore.";
write(client_sock, word2 , strlen(word2));
}
if(strcmp(client_message,"STORE")==0)
{
char word[256]="200 OK STORE";
write(client_sock, word , strlen(word));
}
}
if(read_size == 0)
{
puts("Client disconnected");
fflush(stdout);
}
else if(read_size == -1)
{
printf("recv failed");
}
return 0;
}
最佳答案
您应该在每个 printf
格式控制字符串的末尾添加一个 \n
和/或调用 fflush(3)
您程序中的主要问题是错误地认为给定的发送
(或写入
)到socket(7)对应于另一侧的单个recv
(或read
)。
A tcp(7)实现只是一个字节流;没有隐式消息边界。因此,您的应用程序应该缓冲并将该流显式拆分为有意义的消息。
它可能发生,而且确实发生,一方能够发送
或写入
4000字节,然后是另一个 1000 字节的发射,但另一方将首先 recv
(或 read
),例如39 个字节,然后是 1 个字节,然后是 1960 个字节,然后是 3000 个字节。然后你就会明白“消息”并不是完整的,只有字节流。
另一个困难是这种行为不可重现。下次您运行服务器和客户端程序时,它们的行为会有所不同!
在实践中,很好地定义消息边界在哪里是很有帮助的。你可以像 HTTP 那样做,即在每条消息的开头放置一些内容长度(在 HTTP 中,Content-Length:
属性在回复 header 中)。您还可以决定一条消息是一条长行,以一个换行符终止它(然后,您可能需要一个约定来转义内部换行符),例如在JSON .当然,您应该在两侧进行缓冲。
您可能想通过类似 poll(2) 的多路复用调用来测试套接字(或管道)的可读性或可写性(也许在你的 event loop 中)
我强烈建议阅读 Advanced Linux Programming还有一些Linux Socket Tutorial .
顺便说一句,总是测试你的系统调用是否失败。参见 intro(2)并使用 perror(3)或 errno(3) (通常与 strerror(3) 一起)。
当然编译所有警告和调试信息 (gcc -Wall -g
) 并使用调试器 (gdb
)(也许还有 strace(1) )。您可能希望在不同终端使用两个 gdb
session (一个用于服务器,一个用于客户端)进行调试。
关于c - C 中的基本套接字编程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26085810/