使用 sock_stream 时连续调用 write 是否安全?
is it safe to call write successively when using a sock_stream?
我需要在 Linux 上用 C 编写一个小的 client/server 应用程序。
由于我是网络编程的新手,所以我构建了一个简短的示例以便进行更多探索。
我基本上是在尝试发送一个由客户端动态分配的 double
数组。
我找到了以下方法(客户端):
write(sd,&datas.size,sizeof(int)); /* send size */
write(sd,datas.yi,datas.size*sizeof(double));/* send array */
在服务器端:
read(sd_cli,&datas.size,sizeof(int)); /* receive size */
datas.yi=(double *)malloc(datas.size*sizeof(double));
read(sd_cli,datas.yi,datas.size*sizeof(double)); /* receiving datas */
乍一看我的代码似乎工作正常。
但是由于 write
调用是非阻塞的,我问自己 read
序列是否可以接收,例如 double
的数组在其大小之前?
是否可以保证这种情况永远不会发生?
谢谢。
SOCK_STREAM
类型的套接字提供可靠、有序的数据传输,但细节取决于底层传输的性质。 reader 将接收所有成功写入的数据(如果它实际上选择全部读取),按写入顺序逐字节接收,但不一定是相同大小的块。
阻塞vs。非阻塞与它无关,尽管我实际上没有看到是什么让您说您的写入是非阻塞的。也许您是在评论 write()
和 read()
都没有承诺传输任何给定调用所请求的全部字节数。这本身并不能保证不会发生阻塞,但是您绝对需要正确地考虑它,尤其是对于套接字,尤其是当您确实将套接字的一端或两端置于非阻塞模式时。你的问题的原始版本似乎声称你确实解释了它。
在任何情况下,除非存在某种内核错误,您的客户端永远不会在数组的任何部分之后读取数组大小,也不会以与写入时不同的相对顺序接收字节。
然而,为了完全清楚,这里是通过流套接字读取和写入可变大小 double
数组的合理实现。他们假定发送方和接收方具有相同的 double
类型表示,对于 UNIX 域套接字来说肯定是这种情况。它们一点也不简单,尽管包含大约一半代码的辅助函数适合重用:
/******
* helper functions
*/
/*
* Returns the number of bytes written, which may be zero, or a number
* less than zero on failure.
*/
ssize_t write_fully(int fd, const void *buf, size_t count) {
const unsigned char *next = buf;
size_t remaining = count;
while (remaining) {
ssize_t n_written = write(fd, next, remaining);
if (n_written < 0) {
/* error */
return n_written;
} else {
assert(n_written <= remaining);
next += n_written;
remaining -= n_written;
}
}
/* all bytes successfully written */
return count;
}
/*
* Returns the number of bytes read on success, or a number less
* than zero on error. It is accounted "success" if the end of the stream
* is reached before the requested number of bytes is read; that case
* can be distinguished by the return value, but no recovery is
* possible.
*/
ssize_t read_fully(int fd, void *buf, size_t count) {
unsigned char *next = buf;
size_t remaining = count;
while (remaining) {
ssize_t n_read = read(fd, next, remaining);
if (n_read < 0) {
/* error */
return n_read;
} else if (n_read) {
assert(n_read <= remaining);
next += n_read;
remaining -= n_read;
} else {
/* premature end of file */
return count - remaining;
}
}
/* all bytes successfully read */
return count;
}
/******
* Array-transfer functions
*/
/* returns 0 on success, else nonzero */
int write_double_array(int fd, unsigned n, double d[n]) {
ssize_t bytes_written;
bytes_written = write_fully(fd, &n, sizeof(n));
if (bytes_written < 0) return bytes_written;
bytes_written = write_fully(fd, d, n * sizeof(double));
return (bytes_written < 0) ? bytes_written : 0;
}
/*
* returns 0 on success, else nonzero.
* On success, the caller takes responsibility for freeing the
* dynamically-allocated result array.
*/
int read_double_array(int fd, unsigned *n, double **d) {
unsigned temp_n;
ssize_t bytes_read = read_fully(fd, &temp_n, sizeof(temp_n));
if (bytes_read < 0) {
return -1;
} else if (bytes_read != sizeof(temp_n)) {
return 1;
} else if (temp_n) {
size_t n_bytes = temp_n * sizeof(double);
double *temp = malloc(n_bytes);
if (!temp) return -1; /* allocation failure */
if (read_fully(fd, temp, n_bytes) < n_bytes) {
free(temp);
return -1;
}
/* success */
*d = temp;
}
*n = temp_n;
return 0;
}
您可以以不同的方式实现数组传输协议,但该方法以您声称的相同形式发送数据。你不能安全地做比这更简单的事情。
我需要在 Linux 上用 C 编写一个小的 client/server 应用程序。
由于我是网络编程的新手,所以我构建了一个简短的示例以便进行更多探索。
我基本上是在尝试发送一个由客户端动态分配的 double
数组。
我找到了以下方法(客户端):
write(sd,&datas.size,sizeof(int)); /* send size */
write(sd,datas.yi,datas.size*sizeof(double));/* send array */
在服务器端:
read(sd_cli,&datas.size,sizeof(int)); /* receive size */
datas.yi=(double *)malloc(datas.size*sizeof(double));
read(sd_cli,datas.yi,datas.size*sizeof(double)); /* receiving datas */
乍一看我的代码似乎工作正常。
但是由于 write
调用是非阻塞的,我问自己 read
序列是否可以接收,例如 double
的数组在其大小之前?
是否可以保证这种情况永远不会发生?
谢谢。
SOCK_STREAM
类型的套接字提供可靠、有序的数据传输,但细节取决于底层传输的性质。 reader 将接收所有成功写入的数据(如果它实际上选择全部读取),按写入顺序逐字节接收,但不一定是相同大小的块。
阻塞vs。非阻塞与它无关,尽管我实际上没有看到是什么让您说您的写入是非阻塞的。也许您是在评论 write()
和 read()
都没有承诺传输任何给定调用所请求的全部字节数。这本身并不能保证不会发生阻塞,但是您绝对需要正确地考虑它,尤其是对于套接字,尤其是当您确实将套接字的一端或两端置于非阻塞模式时。你的问题的原始版本似乎声称你确实解释了它。
在任何情况下,除非存在某种内核错误,您的客户端永远不会在数组的任何部分之后读取数组大小,也不会以与写入时不同的相对顺序接收字节。
然而,为了完全清楚,这里是通过流套接字读取和写入可变大小 double
数组的合理实现。他们假定发送方和接收方具有相同的 double
类型表示,对于 UNIX 域套接字来说肯定是这种情况。它们一点也不简单,尽管包含大约一半代码的辅助函数适合重用:
/******
* helper functions
*/
/*
* Returns the number of bytes written, which may be zero, or a number
* less than zero on failure.
*/
ssize_t write_fully(int fd, const void *buf, size_t count) {
const unsigned char *next = buf;
size_t remaining = count;
while (remaining) {
ssize_t n_written = write(fd, next, remaining);
if (n_written < 0) {
/* error */
return n_written;
} else {
assert(n_written <= remaining);
next += n_written;
remaining -= n_written;
}
}
/* all bytes successfully written */
return count;
}
/*
* Returns the number of bytes read on success, or a number less
* than zero on error. It is accounted "success" if the end of the stream
* is reached before the requested number of bytes is read; that case
* can be distinguished by the return value, but no recovery is
* possible.
*/
ssize_t read_fully(int fd, void *buf, size_t count) {
unsigned char *next = buf;
size_t remaining = count;
while (remaining) {
ssize_t n_read = read(fd, next, remaining);
if (n_read < 0) {
/* error */
return n_read;
} else if (n_read) {
assert(n_read <= remaining);
next += n_read;
remaining -= n_read;
} else {
/* premature end of file */
return count - remaining;
}
}
/* all bytes successfully read */
return count;
}
/******
* Array-transfer functions
*/
/* returns 0 on success, else nonzero */
int write_double_array(int fd, unsigned n, double d[n]) {
ssize_t bytes_written;
bytes_written = write_fully(fd, &n, sizeof(n));
if (bytes_written < 0) return bytes_written;
bytes_written = write_fully(fd, d, n * sizeof(double));
return (bytes_written < 0) ? bytes_written : 0;
}
/*
* returns 0 on success, else nonzero.
* On success, the caller takes responsibility for freeing the
* dynamically-allocated result array.
*/
int read_double_array(int fd, unsigned *n, double **d) {
unsigned temp_n;
ssize_t bytes_read = read_fully(fd, &temp_n, sizeof(temp_n));
if (bytes_read < 0) {
return -1;
} else if (bytes_read != sizeof(temp_n)) {
return 1;
} else if (temp_n) {
size_t n_bytes = temp_n * sizeof(double);
double *temp = malloc(n_bytes);
if (!temp) return -1; /* allocation failure */
if (read_fully(fd, temp, n_bytes) < n_bytes) {
free(temp);
return -1;
}
/* success */
*d = temp;
}
*n = temp_n;
return 0;
}
您可以以不同的方式实现数组传输协议,但该方法以您声称的相同形式发送数据。你不能安全地做比这更简单的事情。