TCP服务器中的多条消息出现问题



如果这个问题看起来微不足道,我很抱歉,但我是C语言和套接字编程的初学者。此外,英语不是我的母语。

我最近的一次uni分配失败了,该分配要求我在C中实现一个既接受ipv4又接受ipv6的TCP服务器客户端,在该客户端中,客户端将发送消息来添加、删除、列出和查询服务器中存储的链表中的int对。因此,我用以下代码制作了服务器和客户端(未包括链表和函数strsplit的代码(:

服务器(servidor.c(

void func(int sockfd)
{
struct Node* posts = NULL;
char client_message[MAX];
char server_message[MAX];
char* str;
char **p;
int X, Y, i, j, k;
int n = 0;
char sX[5];
char sY[5];
for (;;)
{
recv(sockfd, client_message, sizeof(client_message), 0);
printf("> %s", client_message);
strcpy(str, client_message);
p = strsplit(str, " ");
memset(server_message, 0, MAX);
if(strncmp("kill", p[0], 4) == 0)
{
break;
}
else if(strncmp("add", p[0], 3) == 0)
{
if(n == 50)
{
strncat(server_message, "limit exceeded", 14);
}
else
{
strncat(server_message, p[1], 4);
strncat(server_message, " ", 1);
X = atoi(p[1]);
strtok(p[2], "n");
strncat(server_message, p[2], 4);
strncat(server_message, " ", 1);
Y = atoi(p[2]);
i = push(&posts, X, Y);
if(i == 0)
{
strncat(server_message, "added", 5);
n++;
}
else
{
strncat(server_message, "already exists", 14);
}
free(p);
}
}
else if(strncmp("rm", p[0], 2) == 0)
{
strncat(server_message, p[1], 4);
strncat(server_message, " ", 1);
X = atoi(p[1]);
strtok(p[2], "n");
strncat(server_message, p[2], 4);
strncat(server_message, " ", 1);
Y = atoi(p[2]);
i = deleteNode(&posts, X, Y);
if(i == 0)
{
strncat(server_message, "removed", 7);
n--;
}
else
{
strncat(server_message, "does not exist", 14);
}
free(p);
}
else if(strncmp("list", p[0], 4) == 0)
{
if(posts == NULL)
{
strncat(server_message, "none", 4);
}
else
{
struct Node *temp = posts;
while(temp != NULL)
{
bzero(sX, 4);
bzero(sY, 4);
snprintf(sX, sizeof(sX), "%d",temp->X);
snprintf(sY, sizeof(sY), "%d",temp->Y);
strncat(server_message, sX, 4);
strncat(server_message, " ", 1);
strncat(server_message, sY, 4);
strncat(server_message, " ", 1);
temp = temp->next;
}
free(temp);
}
}
else if(strncmp("query", p[0], 5) == 0)
{
if(posts == NULL)
{
strncat(server_message, "none", 4);
}
else
{
struct Node *temp1 = posts;
k = 19998;
X = atoi(p[1]);
strtok(p[2], "n");
Y = atoi(p[2]);
while(temp1 != NULL)
{
i = abs(temp1->X - X);
j = abs(temp1->Y - Y);
i = i + j;
if(i <= k)
{
snprintf(sX, sizeof(sX), "%d",temp1->X);
snprintf(sY, sizeof(sY), "%d",temp1->Y);
k = i;
}
temp1 = temp1->next;
}
strncat(server_message, sX, 4);
strncat(server_message, " ", 1);
strncat(server_message, sY, 4);
strncat(server_message, " ", 1);
free(temp1);
}
}
strncat(server_message, "n", 1);
printf("%s", server_message);
send(sockfd, server_message, sizeof(server_message), 0);
}
}
int main(int argc, char *argv[])
{
if(strncmp("v4", argv[1], 2) == 0)
{
int sockfd, connfd, len;
struct sockaddr_in servaddr, cli;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
exit(0);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(atoi(argv[2]));
if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0)
{
exit(0);
}
if ((listen(sockfd, 5)) != 0)
{
exit(0);
}
len = sizeof(cli);
connfd = accept(sockfd, (SA*)&cli, &len);
if (connfd < 0)
{
exit(0);
}
func(connfd);
close(sockfd);
}
else if(strncmp("v6", argv[1], 2) == 0)
{
int sockfd, connfd, len;
struct sockaddr_in6 servaddr, cli;
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
if(sockfd == -1)
{
exit(0);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin6_family = AF_INET6;
servaddr.sin6_addr = in6addr_any;
servaddr.sin6_port = htons(atoi(argv[2]));
if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0)
{
exit(0);
}
if ((listen(sockfd, 5)) != 0)
{
exit(0);
}
len = sizeof(cli);
connfd = accept(sockfd, (SA*)&cli, &len);
if (connfd < 0)
{
exit(0);
}
func(connfd);
close(sockfd);
}
return 0;
}

客户端

void func(int sockfd)
{
char buff[MAX];
int n;
for (;;)
{
bzero(buff, sizeof(buff));
n = 0;
while ((buff[n++] = getchar()) != 'n')
;
send(sockfd, buff, sizeof(buff), 0);
if ((strncmp(buff, "kill", 4)) == 0)
{
break;
}
bzero(buff, sizeof(buff));
recv(sockfd, buff, sizeof(buff), 0);
printf("> %s", buff);
}
}
int main(int argc, char *argv[])
{
if(strchr(argv[1], ':') != NULL)
{
int sockfd, connfd;
struct sockaddr_in6 servaddr, cli;
struct in6_addr result;
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
if (sockfd == -1)
{
exit(0);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin6_family = AF_INET6;
inet_pton(AF_INET6, argv[1], &result);
servaddr.sin6_addr = result;
servaddr.sin6_port = htons(atoi(argv[2]));
if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0)
{
exit(0);
}
func(sockfd);
close(sockfd);
}
else
{
int sockfd, connfd;
struct sockaddr_in servaddr, cli;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
exit(0);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(argv[1]);
servaddr.sin_port = htons(atoi(argv[2]));
if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0)
{
exit(0);
}
func(sockfd);
close(sockfd);
}
return 0;
}

在我自己的测试中,通过在不同的终端中打开服务器和客户端并逐个发送消息,服务器似乎按预期工作。然而,我的教授却不这么认为。在任务失败后,他向我提供了用于对我的服务器进行评级的软件的python代码,如下所示:

run_tests.py

def run_basic_tests(exec_path, version, port, msg_type):
address = '127.0.0.1' if version == 'v4' else '::1'
list_dir = os.listdir('tests/in')
list_dir = sorted(list_dir)
for filename in list_dir:
filename = filename.split('.')[0]
_, result_file_path = tempfile.mkstemp()
server = sp.Popen(
[f'exec {exec_path} {version} {port}'], shell=True, stdout=sp.DEVNULL, stderr=sp.DEVNULL)
ret = os.system(
f'python3 client.py {address} {port} {msg_type} < tests/in/{filename}.in > {result_file_path}')
ret = os.WEXITSTATUS(ret)
if ret == 0:
if filecmp.cmp(f'tests/out/{filename}.out', result_file_path, shallow=False):
print(f'{filename}t[OK]')
else:
print(f'{filename}t[FAILED] (diff output)')
else:
print(f'{filename}t[FAILED] (client exited with non-zero code {ret})')
os.remove(result_file_path)
server.kill()

if __name__ == '__main__':
if len(sys.argv) != 3:
print(f'usage: python3 {sys.argv[0]} <server> <port>')
sys.exit(0)
exec_path = sys.argv[1]
port = int(sys.argv[2])
if not exec_path.startswith('/'):
print('provide the full path to the executable')
sys.exit(0)
for address_family in ['v4', 'v6']:
for msg_type in ['single_msg_single_pkg', 'single_msg_multiple_pkg', 'multiple_msg_single_pkg']:
print('Testing IP' + address_family, msg_type)
run_basic_tests(exec_path, address_family, port, msg_type)

客户.py

BUFFER = ''

def get_address_family(address):
try:
socket.inet_pton(socket.AF_INET, address)
return socket.AF_INET
except:
pass
try:
socket.inet_pton(socket.AF_INET6, address)
return socket.AF_INET6
except:
pass
sys.exit(1)

def create_socket(address, port):
address_family = get_address_family(address)
attempts = 0
client_socket = None
while attempts < 5:
try:
client_socket = socket.socket(address_family, socket.SOCK_STREAM)
client_socket.connect((address, port))
break
except socket.error:
client_socket = None
time.sleep(0.05)
attempts += 1
if not client_socket:
sys.exit(2)
return client_socket

# send one message
def send(client_socket, msg):
msg = msg.encode('ascii')
total_sent = 0
while total_sent != len(msg):
sent = client_socket.send(msg[total_sent:])
if sent == 0:
sys.exit(3)
total_sent += sent

# receive one complete message
def receive(client_socket):
global BUFFER
while True:
if 'n' in BUFFER:
msg = BUFFER[:BUFFER.index('n')]
BUFFER = BUFFER[BUFFER.index('n') + 1:]
return msg
try:
data = client_socket.recv(500 - len(BUFFER)).decode()
except socket.timeout:
sys.exit(7)

if not data:
sys.exit(4)
BUFFER += data
if len(BUFFER) >= 500:
sys.exit(5)

def run_multiple_msg_single_pkg(client_socket):
msg_buffer = []
for msg in sys.stdin:
msg_buffer.append(msg)
for i in range(0, len(msg_buffer), 2):
if i == len(msg_buffer) - 1:
send(client_socket, msg_buffer[i])
if msg_buffer[i] == 'killn':
client_socket.close()
sys.exit(0)
ret = receive(client_socket)
print(ret)
else:
msg = msg_buffer[i] + msg_buffer[i + 1]
send(client_socket, msg)
if msg_buffer[i] == 'killn':
client_socket.close()
sys.exit(0)
ret = receive(client_socket)
print(ret)
if msg_buffer[i + 1] == 'killn':
client_socket.close()
sys.exit(0)
ret = receive(client_socket)
print(ret)

def run_single_msg_multiple_pkg(client_socket):
for msg in sys.stdin:
send(client_socket, msg[:3])
time.sleep(0.1)
send(client_socket, msg[3:])
if msg == 'killn':
client_socket.close()
sys.exit(0)
ret = receive(client_socket)
print(ret)

def run_single_msg_single_pkg(client_socket):
for msg in sys.stdin:
send(client_socket, msg)
if msg == 'killn':
client_socket.close()
sys.exit(0)
ret = receive(client_socket)
print(ret)

if __name__ == '__main__':
if len(sys.argv) != 4:
print(
f'Usage: python3 {sys.argv[0]} <address> <port> [single_msg_single_pkg | single_msg_multiple_pkg | multiple_msg_single_pkg]')
sys.exit(6)
client_socket = create_socket(sys.argv[1], int(sys.argv[2]))
client_socket.settimeout(5)
if sys.argv[3] == 'single_msg_single_pkg':
run_single_msg_single_pkg(client_socket)
elif sys.argv[3] == 'single_msg_multiple_pkg':
run_single_msg_multiple_pkg(client_socket)
elif sys.argv[3] == 'multiple_msg_single_pkg':
run_multiple_msg_single_pkg(client_socket)
client_socket.close()
sys.exit(0)

用这个软件运行我的代码,所有的测试都不起作用。基本上,在接收和发送第一条消息后,我的服务器会停止与客户端的通信,每次测试都失败。这可能与我如何处理代码中的字符串有关,但老实说,我在这里不知所措。至少有人能把我推向正确的方向吗?

主要问题是从python测试应用程序发送的字符串不是以null终止的,但您认为它是。

在python中:

msg = msg.encode('ascii')

msg以ASCII格式编码,但encode()方法返回一个bytes对象,该对象不是null终止的,而是当您在服务器中收到它时:

recv(sockfd, client_message, sizeof(client_message), 0);
printf("> %s", client_message);
strcpy(str, client_message);

您的代码中有一些代码假设您在client_message中收到的是一个以null结尾的字符串。如果在recv()之前将client_message归零,则可能不会看到此问题。

我在你的代码中看到的错误:

  • 您假定消息以null终止。但它们似乎应该是n分离的
  • 始终检查recv的返回。您需要正确处理返回值。你可能会得到错误,或者你可能得到整个数据的一部分

此外,在线程中运行recv循环。虽然这是一个建议,但你通常总是想这样做。

最新更新