c - c语言中的TCP客户端/服务器
问题描述
我创建了一个 TCP 客户端/服务器并提供了测试脚本,但是,除了短消息之外,所有测试都失败了。简单地说,脚本发送客户端通过从文件重定向到服务器读取的任意消息。但是,对于脚本随机创建的文件,它表示接收/发送端的消息不匹配。任何帮助将不胜感激,以下是客户端和服务器代码。
// server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#define QUEUE_LENGTH 10
#define RECV_BUFFER_SIZE 2048
/* TODO: server()
* Open socket and wait for client to connect
* Print received message to stdout
* Return 0 on success, non-zero on failure
*/
int server(char *server_port) {
int sockfd, new_fd;
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address
socklen_t sin_size;
int yes = 1;
char s[INET6_ADDRSTRLEN];
int rv;
char buff[RECV_BUFFER_SIZE];
int numBytes;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my ip address
if ((rv = getaddrinfo(NULL, server_port, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
freeaddrinfo(servinfo); // all done with this structure
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if (listen(sockfd, QUEUE_LENGTH) == -1) {
perror("listen");
exit(1);
}
// printf("server: waiting for connections...\n");
while (1) {
sin_size = sizeof their_addr;
if((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
perror("accept");
continue;
}
if (!fork()) { // child process
close(sockfd); // child does not need the listener
if ((numBytes = recv(new_fd, buff, RECV_BUFFER_SIZE -1, 0)) == -1) {
perror("recv");
exit(1);
}
buff[numBytes] = '\0';
printf("%s", buff);
close(new_fd);
exit(0);
}
close(new_fd);
}
return 0;
}
/*
* main():
* Parse command-line arguments and call server function
*/
int main(int argc, char **argv) {
char *server_port;
if (argc != 2) {
fprintf(stderr, "Usage: ./server-c [server port]\n");
exit(EXIT_FAILURE);
}
server_port = argv[1];
return server(server_port);
}
// client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#define SEND_BUFFER_SIZE 2048
/* TODO: client()
* Open socket and send message from stdin.
* Return 0 on success, non-zero on failure
*/
int client(char *server_ip, char *server_port)
{
int sockfd;
int status;
struct addrinfo hints, *servinfo, *p;
char send_buff[SEND_BUFFER_SIZE];
int numbytes;
char s[INET6_ADDRSTRLEN];
// getaddrinfo
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(server_ip, server_port, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getadrrinfo: %s\n", gai_strerror(status));
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next)
{
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1)
{
perror("client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("client: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "client: failed to connect\n");
return 2;
}
freeaddrinfo(servinfo);
// reading from stdin into send_buff, then send
if((numbytes = read(0, send_buff, SEND_BUFFER_SIZE)) != -1) {
if (send(sockfd, send_buff, numbytes, 0) == -1) {
perror("send");
exit(1);
}
}
close(sockfd);
return 0;
}
/*
* main()
* Parse command-line arguments and call client function
*/
int main(int argc, char **argv)
{
char *server_ip;
char *server_port;
if (argc != 3)
{
fprintf(stderr, "Usage: ./client-c [server IP] [server port] < [message]\n");
exit(EXIT_FAILURE);
}
server_ip = argv[1];
server_port = argv[2];
return client(server_ip, server_port);
}
解决方案
2048 字节的大小超过了典型的MTU(请注意 TCP 位于 IP 之上,它本身是面向数据包的),因此数据很可能通过多个数据包发送。由于您只有一次调用recv
机会,因此您在 TCP/IP 堆栈可以将后续数据包的内容放在那里之前从接收缓冲区获取第一个数据包的内容。
而是在一个循环中进行多次读取并在接收到 0 个字节时退出循环(远程套接字关闭):
while((numBytes = recv(new_fd, buff, RECV_BUFFER_SIZE -1, 0)) > 0)
{
buff[numBytes] = '\0';
printf("%s", buff);
}
if(numBytes < 0)
{
// error handling
}
推荐阅读
- sql - (一个表)获取没有联合的行
- php - Laravel 5.4 - 几个表中的多个关系
- python-3.x - 多处理将 S3 内容主体转换为 pandas DataFrame AWS Sagemaker
- c# - 如何将对象列表从 C# 传递和迭代到 NLua
- javascript - 无法让黑色画布显示在屏幕上
- python - 如何修复“其他”输出超过 1 个结果的问题
- java - 需要在没有xml配置的情况下调用aspect
- c# - 如果 DateTime 为空,如何使用今天的日期?
- git - 如何将 IntelliJ IDEA 连接到我的 GIT 存储库?
- mysql - 使用另一个表中的数据更新行