c - read() 不读取套接字缓冲区上的剩余字节
问题描述
我创建了两个程序,一个客户端和一个服务器。它们通过发送char
固定大小为 115 字节的数组的套接字进行通信。
我要传输的数据存储在以下位置struct
:
typedef struct {
char origin[14];
char type;
char data[100];
} socket_data;
但是为了发送序列化的数据,我想在一个连接所有字段的字符串中发送该信息,struct
所以我发送了一个 115 字节的字符串。如果这些字段中的任何一个未达到其最大大小,我将手动填充额外的数组位置\0
。
我创建了两个在客户端和服务器中实现的函数,它们通过套接字发送数据或从套接字接收数据。
这两个函数如下:
void socket_send(int socket, char *origin, char type, char *data) {
char info[115]; //data to be sent
socket_data aux;
strcpy(aux.origin, origin);
aux.type = type;
strcpy(aux.data, data);
//Filling up the remaining positions of origin and data variables
for (int i = (int) strlen(aux.origin); i<14; i++) aux.origin[i] = '\0';
for (int i = (int) strlen(aux.data); i<100; i++) aux.data[i] = '\0';
//Building up the 115 byte string I want to send via socket
for (int i=0; i<14; i++) info[i] = aux.origin[i];
info[14] = type;
for (int i=0; i<100; i++) info[i+15] = aux.data[i];
ssize_t total_bytes = 115;
ssize_t bytes_written = 0;
//Here I send all the bytes through the socket
do {
bytes_written = write(socket, info + (115 - total_bytes), total_bytes);
total_bytes -= bytes_written;
} while (total_bytes > 0);
}
socket_data socket_rcv(int socket) {
socket_data info;
char sequence[115];
ssize_t total_bytes = 115;
ssize_t bytes_read = 0;
//Here I receive all the bytes from the socket (till I fill up the 115 byte string called sequence)
do {
bytes_read = read(socket, sequence + (115 - total_bytes), total_bytes);
total_bytes -= bytes_read;
} while (total_bytes > 0);
//Then I return a stuct
for (int i=0; i<14; i++) info.origin[i] = sequence[i];
info.type = sequence[14];
for (int i=0; i<100; i++) info.data[i] = sequence[i+15];
return info;
}
如您所见,我循环read()
并write()
确保发送所有字节,因为我知道有时这些函数读取或写入的字节数少于要求的字节数。
问题是,在测试程序的功能时,我发现在读取的字节数较少(它循环)的情况下,程序会阻塞(可能等待write()
来自服务器端的另一个字节)而不是读取剩余的字节套接字缓冲区(因为发送了所有 115 个字节而只接收了 111 个字节,所以套接字缓冲区中应该还有 4 个字节)。有时,程序不是阻塞等待可能write()
的,而是在它不应该终止时终止......
我在这里找不到问题,我会很感激一些帮助
编辑
我创建了这个函数来设置套接字......
服务器:
int socketConfig (connection_info cinfo) {
int socketfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socketfd < 0) {
write(1, "Socket error\n", strlen("Socket error\n"));
return -1;
}
struct sockaddr_in s_addr;
memset (&s_addr, 0, sizeof (s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(cinfo.port);
s_addr.sin_addr.s_addr = INADDR_ANY;
if (bind (socketfd, (void *) &s_addr, sizeof (s_addr)) < 0) {
write(1, "Bind error\n", strlen("Bind error\n"));
return -1;
}
listen(socketfd, 3);
return socketfd;
}
int receiveClient(int serverfd) {
struct sockaddr_in client;
socklen_t len = sizeof(client);
return accept(serverfd, (void *) &client, &len);
}
客户:
int connect_to_server(Config config) {
struct sockaddr_in client;
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
write(1, "Connecting Jack...\n", strlen("Connecting Jack...\n"));
if (sockfd < 0) {
write(1, "Error creating the socket\n", strlen("Error creating the socket\n"));
return -1;
}
memset(&client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(config.port_jack);
if (inet_aton(config.ip_jack, &client.sin_addr) == 0) {
write(1, "Invalid IP address\n", strlen("Invalid IP address\n"));
return -1;
}
if (connect(sockfd, (void *) &client, sizeof(client)) < 0) {
write(1, "Error connecting to Jack\n", strlen("Error connecting to Jack\n"));
return -1;
}
return sockfd;
}
我可以保证连接正常
解决方案
您没有检查失败的返回值write()
和read()
失败。
尝试更多类似的东西:
int socket_send_all(int socket, const void *data, size_t size) {
const char *pdata = (const char*) data;
ssize_t bytes_written;
while (size > 0) {
bytes_written = write(socket, pdata, size);
if (bytes_written < 0) return bytes_written;
pdata += bytes_written;
size -= bytes_written;
}
return 0;
}
int socket_rcv_all(int socket, void *data, size_t size) {
char *pdata = (char*) data;
ssize_t bytes_read;
while (size > 0) {
bytes_read = read(socket, pdata, size);
if (bytes_read <= 0) return bytes_read;
pdata += bytes_read;
size -= bytes_read;
}
return 1;
}
int socket_send2(int socket, const socket_data *sd) {
char bytes[115];
memcpy(bytes, sd->origin, 14);
bytes[14] = sd->type;
memcpy(bytes+15, sd->data, 100);
return socket_send_all(socket, bytes, 115);
/* alternatively:
int ret = socket_send_all(socket, sd->origin, 14);
if (ret == 0) ret = socket_send_all(socket, &(sd->type), 1);
if (ret == 0) ret = socket_send_all(socket, sd->data, 100);
return ret;
*/
}
int socket_send(int socket, char *origin, char type, char *data) {
socket_data aux;
strncpy(aux.origin, origin, 14);
aux.type = type;
strncpy(aux.data, data, 100);
return socket_send2(socket, &aux);
}
int socket_rcv2(int socket, socket_data *sd) {
char bytes[115];
int ret = socket_rcv_all(socket, bytes, 115);
if (ret > 0) {
memcpy(sd->origin, bytes, 14);
sd->type = bytes[14];
memcpy(sd->data, bytes+15, 100);
}
return ret;
/* alternatively:
int ret = socket_rcv_all(socket, sd->origin, 14);
if (ret > 0) ret = socket_rcv_all(socket, &(sd->type), 1);
if (ret > 0) ret = socket_rcv_all(socket, sd->data, 100);
return ret;
*/
}
socket_data socket_rcv(int socket) {
socket_data aux;
int ret = socket_rcv2(socket, &aux);
if (ret <= 0) {
// error handling ...
}
return aux;
}
推荐阅读
- excel - 如何将筛选的行选择设置为范围
- ios - 如何正确格式化字体?
- sql - 如何获取 varchar(MAX) 字符串类型的 XML 标记的值?
- c++ - 我应该在类中使用指向集合的指针吗?
- pandas - 如何在多个值上过滤熊猫中的单列(dtype = object)
- swift - 如何使用户定义的运行时属性在 xcode 10.2.1 和 ios 12.2 中起作用
- java - `p12` 文件在 Firefox RestClient 中有效,但在 WebSphere 中无效
- r - `scale_color_gradient` 中的 `expand` 参数被忽略
- python - 如何改进我在熊猫中的布尔索引?
- javascript - 尝试在 Facebook 上上传图片时出现“(#324)需要上传文件”错误