我必须编写一个包含整数的TCP服务器程序,该程序应该可由客户端程序修改。它应该类似于银行帐户。一切都很好,除了一件事:
当客户端首次连接到服务器时,它将等待欢迎消息(服务器必须是迭代的,因此它应该一次只处理一个客户端)。服务器总是只发送欢迎消息的前几个字母。所有其他消息都完全正确地传输。
在第49行中,首先将欢迎消息复制到char数组,然后写入套接字。这是错误的地方......只发送前1-5个字母(每次新客户端连接时都不同)。在其他我使用sprintf()将消息复制到char数组然后将其写入套接字的地方,一切都像我想要的那样工作。 我也尝试过使用snprintf(),但这也不起作用。我究竟做错了什么? :d
所以这将是客户端的示例输出:
Connected!
Waiting for welcome message...
We
之后,我可以开始向服务器输入命令。但是两封信后,整个欢迎信息被删除了。但如上所述,有时只有一个字母,有时是五个字母:D。
无论如何,这里是我的代码(如果有任何其他错误或我应该避免的事情,请随时告诉我:D):
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define BufferSize 99999
void error(const char *msg) {
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char msg[BufferSize], data[BufferSize];
if (argc < 3) error("usage: <hostname> <port>\n");
server = gethostbyname(argv[1]);
if (server == NULL) error("Host not found!");
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("socket() error");
bzero((char *) &serv_addr, sizeof (serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *) server->h_addr, (char *) &serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) error("connect() error");
printf("Connected!\nWaiting for welcome message...\n");
memset(msg, 0, BufferSize);
n = read(sockfd, msg, BufferSize - 1);
if (n < 0) error("read() error");
printf("%s\n", msg);
memset(data, 0, BufferSize);
while (fgets(data, BufferSize, stdin) != NULL) {
data[strlen(data) - 1] = '\0'; //remove trailing newline char
n = write(sockfd, data, strlen(data) + 1);
if (n < 0) error("write() error");
if (strcmp(data, "exit") == 0) break;
memset(msg, 0, BufferSize);
n = read(sockfd, msg, BufferSize - 1);
if (n < 0) error("read() error");
if (n==0) error("Server shut down...");
printf("%s\n", msg);
memset(data, 0, BufferSize);
}
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>
#include <limits.h>
#define BufferSize 99999
#define ClientWaiting 100
void error(const char *msg) {
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
int sockfd, newsockfd, portno, n, amount, balance, balOld;
socklen_t clilen;
char msg[BufferSize], data[BufferSize], *splitBuf[2];
struct sockaddr_in serv_addr, cli_addr;
balance = 0;
if (argc < 2) error("usage: <port>");
portno = atoi(argv[1]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("socket() error");
memset(&serv_addr, 0, sizeof (serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) error("bind() error");
listen(sockfd, ClientWaiting);
while (1) {
printf("Waiting for new client...\n");
clilen = sizeof (cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) error("accept() error");
printf("New connection accepted...\n");
memset(data, 0, BufferSize);
sprintf(data, "Welcome!\nPlease use the following commands:\n<put, get> <positive integer>\nBalance: %d€", balance);
n = write(newsockfd, data, strlen(msg) + 1);
if (n < 0) error("write() error");
while (1) {
splitBuf[0] = NULL;
splitBuf[1] = NULL;
memset(data, 0, BufferSize);
memset(msg, 0, BufferSize);
n = read(newsockfd, msg, BufferSize - 1);
if (n < 0) {
fprintf(stderr, "read() error\n");
break;
}
if (n == 0) {
printf("Client disconnected...\n");
break;
}
printf("Message received: %s\n", msg);
if (strcmp(msg, "exit") == 0) break;
splitBuf[0] = strtok(msg, " ");
splitBuf[1] = strtok(NULL, " ");
if (splitBuf[1] == NULL) {
strcpy(data, "Please use the following commands:\n<put, get> <positive integer>");
} else {
amount = atoi(splitBuf[1]);
if (amount <= 0) {
strcpy(data, "Please use the following commands:\n<put, get> <positive integer>");
} else if (strcmp(splitBuf[0], "put") == 0) {
balOld = balance;
balance += amount;
if (balance < balOld) {
balance = INT_MAX;
sprintf(data, "Warning! Overflow!\nBalance: %d€", balance);
} else {
sprintf(data, "Balance: %d€", balance);
}
printf("New balance: %d€\n", balance);
} else if (strcmp(splitBuf[0], "get") == 0) {
balOld = balance;
balance -= amount;
if (balance > balOld) {
balance = INT_MIN;
sprintf(data, "Warning! Underflow!\nBalance: %d€", balance);
} else {
sprintf(data, "Balance: %d€", balance);
}
printf("New balance: %d€\n", balance);
}
}
n = write(newsockfd, data, strlen(data) + 1);
if (n < 0) error("write() error");
}
close(newsockfd);
}
close(newsockfd);
close(sockfd);
return 0;
}
答案 0 :(得分:2)
对于SO上的人来说,这个例子太长了。 (我们不是编译器和调试器。)
您的第一步必须是将程序分解为可以独立调试的更小,更容易理解的部分。有几种方法可以做到这一点:
例如,我看看:
splitBuf[0] = strtok(msg, " ");
splitBuf[1] = strtok(NULL, " ");
splitBuf是否包含您的期望? (NULL是strtok的有效参数吗?)
我推荐两件事: #include
# Are my assumptions met?
assert( splitBuf[0]!=null );
assert( splitBuf[1]!=null );
#ifdef DEBUG
printf("splitBuf[0]=%s\n", splitBuf[0]);
printf("splitBuf[1]=%s\n", splitBuf[1]);
#endif
使用-DDEBUG = 1进行编译以确保定义了DEBUG,或者添加:
#define DEBUG
位于文件顶部。
其次,如果您解决较小的问题,编程会更容易,然后独立地处理并测试您对这些问题的答案。我们假设您需要解析来自网络的消息并提取余额,然后您可以写下:
int parseBalance(char const* serverMessage) {
...
return balance;
}
您现在可以编写测试:
void tests()
{
// test parseBalance
assert( 100 == parseBalance("100") )
... more tests
}
最低限度,您可以在程序开始时调用tests()来执行自检(阅读&#34;单元测试&#34;以获得更好的方法)。
如果您以这种方式编程:
答案 1 :(得分:1)
我相信你的问题出现在你的第一个read()调用中。你做了一次阅读,希望得到所有的欢迎信息。但这不是TCP的工作方式。 TCP根据自己的内部规则将流数据放入数据包,接收系统可以使其以任何需要的数量提供。
您无法依靠获取服务器在一次读取中写入的所有数据。
事实上,服务器也搞砸了。你不能指望一个写通话来写你说的一切。操作系统的套接字发送缓冲区可能已满,或者可能有信号中断了呼叫。
您需要发送和接收缓冲区和函数来处理读取和写入循环,直到您收到完整的行或发送完整的缓冲区为止。