client 如何完整接收server数据
上一章节,client 直接通过一次read 获取server 返回值,但是在最初你并不知道server 给你传递多长字节的数据;因此就必须在应用层协商数据接收的边界;比如收到char Q 的时候就结束本次TCP连接;
第一版应用层协议
设计如下的应用层协议
举个例子: 假设client 输入如下
3
12
24
36
+
表示的意思是client 要传递3个number, 分别是12,24,36要对他们进行加法运算,也就是要server 计算12+24+36=
需要注意以下2点:
- client 传递的oprand count 是1字节,传递的运算符也是1字节,这两个都不需要转换字节序
- operand 是4字节,这个需要传输的时候转换为网络字节序,在本地进行计算的时候要转换成主机序列
这是我server.c 代码
需要注意的点:
63~68 行 对operand 转换成本机字节序计算,因为网络字节序统一是大端序,我的mac是小端序,测试命令 sysctl hw.byteorder
输出是1234
#include <arpa/inet.h> // For network address conversion functions
#include <stdio.h> // For standard input and output functions
#include <stdlib.h> // For general utility functions like exit()
#include <string.h> // For memory manipulation functions like memset()
#include <sys/socket.h> // For socket-related functions and structures
#include <unistd.h> // For close() function
#define BUF_SIZE 1024
#define OPSZ 4 // Operand size (bytes per operand)
void error_handling(char *message);
int calculate(int opnd_cnt, int opnds[], char operator);
int main(int argc, char *argv[]) {
int serv_sock; // Server socket descriptor
int clnt_sock; // Client socket descriptor
char opmsg[BUF_SIZE]; // Store operation message
int result, opnd_cnt, i;
struct sockaddr_in serv_addr; // Server address structure
struct sockaddr_in clnt_addr; // Client address structure
socklen_t clnt_addr_size; // Size of client address structure
if (argc != 2) {
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}
// Create a socket for the server
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if (serv_sock == -1)
error_handling("socket() error");
// Initialize the server address structure
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET; // IPv4
serv_addr.sin_addr.s_addr =
htonl(INADDR_ANY); // Accept connections from any IP
serv_addr.sin_port =
htons(atoi(argv[1])); // Port number in network byte order
// Bind the socket
if (bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
error_handling("bind() error");
// Start listening for incoming connections
if (listen(serv_sock, 5) == -1)
error_handling("listen() error");
// Accept a client connection
clnt_addr_size = sizeof(clnt_addr);
clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
if (clnt_sock == -1)
error_handling("accept() error");
// Read the operand count (1 byte from client)
uint8_t opnd_cnt_byte;
read(clnt_sock, &opnd_cnt_byte, 1);
opnd_cnt = (int)opnd_cnt_byte; // Convert to integer
printf("Operand count received: %d\n", opnd_cnt);
// Read operands (convert from network byte order to host byte order)
int opnds[opnd_cnt];
for (i = 0; i < opnd_cnt; i++) {
int net_operand;
read(clnt_sock, &net_operand, OPSZ); // Read 4 bytes (network byte order)
opnds[i] = ntohl(net_operand); // Convert to host byte order
printf("Operand %d: %d\n", i + 1, opnds[i]);
}
// Read the operator (1 byte from client)
char operator;
read(clnt_sock, &operator, 1);
printf("Operator received: %c\n", operator);
// Perform the calculation
result = calculate(opnd_cnt, opnds, operator);
// result = ntohl(result); // Convert to network byte order
printf("Calculation result: %d\n", result);
// Convert the result to network byte order and send it back to client
int net_result = htonl(result);
write(clnt_sock, &net_result, sizeof(net_result));
// Close sockets
close(clnt_sock);
close(serv_sock);
return 0;
}
void error_handling(char *message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int calculate(int opnd_cnt, int opnds[], char operator) {
int result = opnds[0];
for (int i = 1; i < opnd_cnt; i++) {
switch (operator) {
case '+':
result += opnds[i];
break;
case '-':
result -= opnds[i];
break;
case '*':
result *= opnds[i];
break;
case '/':
if (opnds[i] != 0)
result /= opnds[i];
else {
printf("Division by zero error!\n");
exit(1);
}
break;
default:
printf("Unknown operator: %c\n", operator);
exit(1);
}
}
return result;
}
这是我的client.c 代码
关键点是42~48 行把本地字节序的4字节operand 转换成网络字节序 也就是大端序
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BUF_SIZE 1024
#define RLT_SIZE 4
#define OPSZ 4
void error_handling(char *message);
int main(int argc, char *argv[]) {
int sock;
char opmsg[BUF_SIZE];
int result, opnd_cnt, i;
struct sockaddr_in serv_adr;
if (argc != 3) {
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1)
error_handling("socket() error");
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
serv_adr.sin_port = htons(atoi(argv[2]));
if (connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
error_handling("connect() error!");
else
puts("Connected...........");
fputs("Operand count: ", stdout);
scanf("%d", &opnd_cnt);
opmsg[0] = (char)opnd_cnt;
for (i = 0; i < opnd_cnt; i++) {
int oprand;
printf("Operand %d: ", i + 1);
scanf("%d", &oprand);
oprand = htonl(oprand);
memcpy(&opmsg[i * OPSZ + 1], &oprand, OPSZ);
}
fgetc(stdin);
fputs("Operator: ", stdout);
scanf("%c", &opmsg[opnd_cnt * OPSZ + 1]);
write(sock, opmsg, opnd_cnt * OPSZ + 2);
read(sock, &result, RLT_SIZE);
result = ntohl(result);
printf("Operation result: %d \n", result);
close(sock);
return 0;
}
void error_handling(char *message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
演示结果
启动server
启动client
server输出结果