對于某些背景,我正在撰寫一個 TCP 套接字服務器/客戶端通信程式來將一個文本檔案復制到另一個。我正在努力實作通過套接字讀取的實作。我正在Linux上開發。
這是當客戶端讀取函式通過套接字發送讀取操作碼時呼叫的服務器端讀取函式。程式使用正確的 fd 和計數正確地到達此代碼。
/* s_read
* server side read
* Send in socket: 4 bytes return value, 4 bytes errno, n bytes data
*/
int s_read(int conn){
// Get fd
unsigned char fdstr[4];
int L = sizeof(char) * 4;
int bytes = 0;
int total = 0;
while ( total < L ) {
if ( (bytes = read(conn, fdstr total, L - total)) < 0) {
printf("r_server.c::s_read: Couldn't receive fd\n");
return -1;
}
total = bytes;
}
int fd = 0;
fd = (fdstr[0] << 24) | (fdstr[1] << 16) | (fdstr[2] << 8) | fdstr[3];
// Get count
unsigned char countstr[4];
L = sizeof(char) * 4;
bytes = 0;
total = 0;
while ( total < L ) {
if ( (bytes = read(conn, countstr total, L - total)) < 0) {
printf("r_server.c::s_read: Couldn't receive count\n");
return -1;
}
total = bytes;
}
int count = 0;
count = (countstr[0] << 24) | (countstr[1] << 16) | (countstr[2] << 8) | countstr[3];
// Prepare return message
int return_value;
L = 8 // 4 bytes for return value, 4 bytes for errno
count; // count bytes for read in data
int error = 0;
char * msg;
char * read_value = malloc(count);
// Execute open call
bytes = 0;
total = 0;
while ( total < count ) {
if ( (return_value = read(fd, read_value total, count - total)) < 0 ) {
error = errno;
break;
}
total = return_value;
}
// Build return message
msg = malloc(L);
L=0;
msg[L ] = (return_value >> 24) & 0xff; // put the kernel return value
msg[L ] = (return_value >> 16) & 0xff;
msg[L ] = (return_value >> 8) & 0xff;
msg[L ] = (return_value ) & 0xff;
msg[L ] = (error >> 24) & 0xff; // put the errno
msg[L ] = (error >> 16) & 0xff;
msg[L ] = (error >> 8) & 0xff;
msg[L ] = (error ) & 0xff;
for (int i=0; i < count; i )
msg[L ] = read_value[i]; // put the read in data.
// Send return message
bytes = 0;
total = 0;
while ( total < L ) {
if ( (bytes = write(conn, msg total, L - total)) < 0) {
printf("r_server.c::s_read: Error sending r_read return value to client\n");
return -1;
}
total = bytes;
}
free(read_value);
free(msg);
return 0;
}
這是客戶端讀取功能,它將有效負載放在一起并通過套接字發送它以請求服務器在其末端讀取。
/* r_read
* remote read
*/
int r_read(int fd, void *buf, int count) {
int L;
char * msg;
int in_msg;
int in_err;
L = 1 // byte for opcode
sizeof(fd) // int bytes for fd.
sizeof(count); // int bytes for count.
msg = malloc(L);
L=0;
msg[L ] = 3; // this is the code for read.
msg[L ] = (fd >> 24) & 0xff; // put the fd.
msg[L ] = (fd >> 16) & 0xff;
msg[L ] = (fd >> 8) & 0xff;
msg[L ] = (fd ) & 0xff;
msg[L ] = (count >> 24) & 0xff; // put the count.
msg[L ] = (count >> 16) & 0xff;
msg[L ] = (count >> 8) & 0xff;
msg[L ] = (count ) & 0xff;
int bytes = 0;
int total = 0;
while ( total < L ) {
if ( (bytes = write(sock, msg total, L - total)) < 0) {
printf("Failed to send r_read to server\n");
return -1;
}
total = bytes;
}
bytes = 0;
total = 0;
while ( total < 8 ) {
if ( (bytes = read(sock, msg total, 8 - total)) < 0) {
printf("Failed to receive r_read from server\n");
return -1;
}
total = bytes;
}
in_msg = (msg[0] << 24) | (msg[1] << 16) | (msg[2] << 8) | msg[3];
in_err = (msg[4] << 24) | (msg[5] << 16) | (msg[6] << 8) | msg[7];
for (int i = 0; i < count; i ) {
*(char *)(buf i) = msg[i 8];
}
errno = in_err;
free(msg);
return in_msg;
}
客戶端在 in_msg 中接收到一個虛假的 return_value,但服務器在發送之前看到了正確的 return_value。實際讀取的資料在兩端似乎也是假的。
This is the code actually calling the i/o functions to copy the one file to the other. I have a similar one that copies from a local file to a remote file, and that one works properly. It is called from main after main receives the socket information and strips those args from argv[].
int entry(int argc, char* argv[]){
// Input guards
if (argc != 3) {
printf("Invalid arguments\n");
return -1;
}
// Get file names
char *filename = argv[1];
char *copyfilename = argv[2];
printf("rclient2.c::entry: Copying remote file %s to local file %s\n", filename, copyfilename);
// Open files
int fd = r_open((const char*) filename, O_RDWR, (mode_t) 0600);
if (fd < 0) {
printf("rclient2.c::entry: r_open failed.\n");
return -1;
}
int copyfd = open((const char*) copyfilename, O_RDWR | O_CREAT, (mode_t) 0600);
if (copyfd < 0) {
printf("rclient2.c::entry: open failed.\n");
return -1;
}
// Seek to position 10
//r_lseek(fd, 10, SEEK_SET); // Later requirement once read is working
// Copy file
char buf;
while ( r_read(fd, &buf, 1) > 0 ) {
if (write(copyfd, &buf, 1) < 0) {
printf("rclient2::entry: write failed.\n");
return -1;
}
}
// Close files
r_close(fd);
close(copyfd);
return 0;
}
關于錯誤行為的更多細節:服務器在關閉之前只收到 s_read() 呼叫 6 次,在這種情況下它應該得到大約 41 次呼叫。在閱讀“復制”檔案時,它充滿了人類無法讀取的虛假值。除了 close() 呼叫之后的一個錯誤之外,我的所有錯誤都沒有被絆倒。特別是“r_server.c::main:無法接收操作碼”。錯誤。因此,我在下面包含了服務器的主要功能(它讀取客戶端功能提供的操作碼)。
/* main - server implementation
*/
int main(int argc, char *argv[]){
// Setup socket data
int listener, conn;
unsigned int length;
struct sockaddr_in s1, s2;
// Create server socket
listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener < 0) {
printf("r_server.c::main: Error creating server socket\n");
return -1;
}
// Establish server socket
bzero((char *) &s1, sizeof(s1));
s1.sin_family = AF_INET;
s1.sin_port = htons(0);
s1.sin_addr.s_addr = inet_addr("127.0.0.1");
if( bind( listener, (struct sockaddr *) &s1, sizeof(s1)) < 0) {
printf("r_server.c::main: Server couldn't bind to the port\n");
return -1;
}
// Print port and start server
length = sizeof(s1);
getsockname(listener, (struct sockaddr *) &s1, &length);
printf("%d\n", s1.sin_port);
if ( listen(listener, 1) < 0) {
printf("r_server.c::main: Server error while listening\n");
return -1;
}
// While server running
while(1) {
// Connect to new client
length = sizeof(s2);
conn = accept(listener, (struct sockaddr *) &s2, &length);
if( conn < 0 ) {
printf("r_server.c::main: Server failed to accept incoming stuff\n");
return -1;
}
// Fork to manage client and listen for new clients
if (fork() == 0) {
// Until client disconnects
while (1) {
// Get opcode
unsigned char opcode;
int success;
if ((success = read(conn, &opcode, sizeof(opcode))) < 0) {
printf("r_server.c::main: Couldn't receive opcode. opcode = %d\n", opcode);
return -1;
}
// Client disconnected
if (success == 0) {
return 0;
}
// Call related server side function
switch (opcode) {
case (1):
printf("Opening...\n");
if (s_open(conn) < 0)
return -1;
break;
case (2):
printf("Closing...\n");
if (s_close(conn) < 0)
return -1;
break;
case (3):
printf("Reading...\n");
if (s_read(conn) < 0)
return -1;
break;
case (4):
printf("Writing...\n");
if (s_write(conn) < 0)
return -1;
break;
case (5):
printf("Seeking...\n");
if (s_lseek(conn) < 0)
return -1;
break;
case (6):
printf("Piping...\n");
if (s_pipe(conn) < 0)
return -1;
break;
case (7):
printf("Duping...\n");
if (s_dup2(conn) < 0)
return -1;
break;
default:
return -1;
}
}
return 0;
}
}
return 0;
}
uj5u.com熱心網友回復:
該代碼盲目地假設read將始終準確讀取所要求的位元組數并write始終寫入給定的完整緩沖區。這兩個假設都是錯誤的。
read只會讀取到給定的大小,但實際上可能讀取的更少。類似的最多write只會寫入完整的緩沖區,但可能會寫入更少。因此必須檢查回傳值以找出實際讀取或寫入的資料量。如果不是所有內容都被讀取或寫入,則必須對剩余資料重復操作,甚至可能多次。
uj5u.com熱心網友回復:
在 r_read() 函式中,我們從未讀入服務器放入回應中的值。我們只讀取與 return_value 和 errno 對應的 8 個位元組。
代替
int bytes = 0;
int total = 0;
while ( total < L ) {
if ( (bytes = write(sock, msg total, L - total)) < 0) {
printf("Failed to send r_read to server\n");
return -1;
}
total = bytes;
}
bytes = 0;
total = 0;
while ( total < 8 ) {
if ( (bytes = read(sock, msg total, 8 - total)) < 0) {
printf("Failed to receive r_read from server\n");
return -1;
}
total = bytes;
}
in_msg = (msg[0] << 24) | (msg[1] << 16) | (msg[2] << 8) | msg[3];
in_err = (msg[4] << 24) | (msg[5] << 16) | (msg[6] << 8) | msg[7];
for (int i = 0; i < count; i ) {
*(char *)(buf i) = msg[i 8];
}
我們需要做
int bytes = 0;
int total = 0;
while ( total < L ) {
if ( (bytes = write(sock, msg total, L - total)) < 0) {
printf("Failed to send r_read to server\n");
return -1;
}
total = bytes;
}
bytes = 0;
total = 0;
while ( total < 8 ) {
if ( (bytes = read(sock, msg total, 8 - total)) < 0) {
printf("Failed to receive r_read from server\n");
return -1;
}
total = bytes;
}
in_msg = (msg[0] << 24) | (msg[1] << 16) | (msg[2] << 8) | msg[3]; // # of bytes remaining in the socket buffer (the data we asked for)
in_err = (msg[4] << 24) | (msg[5] << 16) | (msg[6] << 8) | msg[7];
bytes = 0;
total = 0;
while ( total < in_msg ) {
if ( (bytes = read(sock, buf total, in_msg - total)) < 0) {
printf("Failed to read the r_read data from server\n");
return -1;
}
}
請注意,while ( total ...)僅當您需要處理讀取或寫入呼叫未完全成功的情況時才需要該邏輯。就我而言,如果發生這種情況,我們可以簡單地退出并顯示錯誤。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/371991.html
