[英]C socket programming - Read and Write of TCP Server and Client out of sync
我在 C 中有一个简单的服务器和客户端。
我要实现的功能的步骤是:
服务器向客户端发送一个字符“%”,并打印在客户端的屏幕上。
然后客户端输入一条消息,该消息将发送到服务器并打印在服务器的屏幕上,上面写着“客户端:(来自客户端的消息)”。
服务器最终向客户端发送一条消息“收到消息”,并将其打印在客户端的屏幕上。
返回步骤 1。
我添加了一个名为valid for client 的指示器,用于告诉服务器缓冲区(来自客户端的消息)何时实际有效,以防止客户端写入和服务器读取不同步。
然而,我得到的结果是这样的:
先感谢您 !
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void error(const char *msg){
perror(msg);
exit(1); //terminate program
}
int main(int argc, char *argv[]){
int sockfd/*(file descriptor)*/, portno, n;
char buffer[255];
int valid = 0;
struct sockaddr_in serv_addr, cli_addr;
portno = 1234;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0){
error("Error opening socket.");
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(1234);
if( connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0 ){
error("Connection failed");
}
S: bzero(buffer, 255);
n = read(sockfd, buffer, 255);
if(n < 0){
error("Error reading from socket");
}
printf("%s", buffer);
bzero(buffer, 255);
fgets(buffer, 255, stdin);
// valid is set to 1 and sent to server
// when the message in buffer is actually typed in and meant to be sent to the server
valid = 1;
write(sockfd, &valid, sizeof(valid));
write(sockfd, buffer, strlen(buffer));
if(strncmp("exit", buffer, 4) == 0){
goto Q;
}
bzero(buffer, 255);
n = read(sockfd, buffer, 255);
if(n < 0){
error("Error reading from socket");
}
printf("%s", buffer);
goto S;
Q: printf("You have chosen to exit. Exit successful\n");
close(sockfd);
return 0;
}
服务器代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void error(const char *msg){
perror(msg);
exit(1); //terminate program
}
int main(int argc, char *argv[]){
int sockfd/*(file descriptor)*/, newsockfd, portno, n;
char buffer[255];
int valid = 0;
int count = 0;
struct sockaddr_in serv_addr, cli_addr;
socklen_t clilen;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0){
error("Error opening socket.");
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(1234);
if(bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
error("Building failed");
}
listen(sockfd, 5); //integer stands for max clients
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if(newsockfd < 0){
error("Error on accept");
}
int num1, num2, ans, choice;
S:
valid = 0;
bzero(buffer, 255);
if(count == 0){
count++;
strcpy(buffer, "********************************\n** Welcome to the BBS server. **\n********************************\n%");
}else{
strcpy(buffer, "% ");
}
n = write(newsockfd, buffer, strlen(buffer));
if(n < 0){
error("Error writing to socket");
}
M:
// check if the message from client (buffer) is valid
read(newsockfd, &valid, sizeof(int));
// if not, return to M
if(valid == 0){
goto M;
}
// continue if valid
bzero(buffer, 255);
read(newsockfd, buffer, strlen(buffer));
if(strncmp("exit", buffer, 4) == 0){
goto Q;
}
printf("Client: %s\n", buffer);
bzero(buffer, 255);
strcpy(buffer, "Message received.\n");
n = write(newsockfd, buffer, strlen(buffer));
if(n < 0){
error("Error writing to socket\n");
}
goto S;
Q: close(newsockfd);
close(sockfd);
return 0;
}
您的代码有很多问题,但导致您询问的特定不当行为的问题来自服务器中的此代码:
bzero(buffer, 255); read(newsockfd, buffer, strlen(buffer));
缓冲区刚刚清零,您可以确信strlen(buffer)
将返回 0。因此read()
实际上不会传输任何字节。 你可能想要sizeof
而不是strlen
:
bzero(buffer, 255);
read(newsockfd, buffer, sizeof(buffer));
代码中很明显的其他问题包括
假设每一方的write()
调用将与另一方的read()
调用一对一配对以传输完整的消息。 您使用的是流套接字,因此实际上没有消息边界。 通常会看到一次read
从多个write
接收字节,有时一次write
的字节会拆分为两个甚至更多read
。 因此,
如果您想以完整消息为单位交换数据,那么您需要在流的顶部分层,使参与者能够识别消息边界。
每一方都必须准备好接收部分消息。
您传输的valid
信号值并不足以确保客户端和服务器保持同步。
read
和write
调用不能保证传输指定的完整字节数。 如果您想传输特定数量的字节,那么您必须使用这些函数的返回值来确定您是否真的这样做了,如果不是,则需要传输多少字节。
客户端和服务器在每次read
最多接收缓冲区的完整大小,并且他们假设此后可以将缓冲区内容作为字符串处理。 但是,如果read
实际上读取了最大数量的字节(并且没有嵌入的空字节),那么缓冲区将不会终止,并且其内容可以被视为字符串的假设将导致未定义的行为。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.