會不定期地報10053/10054的問題。將接收的資料大小和標志位列印出來,每次報錯都是因為接收不完整導致的,但是不明白為什么會這樣以及如何修改。linux server端接收的資訊如下:

不知為何接收到的資料大小會忽然變化,之后就會導致錯誤。而且是不定期的!有時候能夠完整傳輸!
代碼:
linux server:(recv)
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#define SERVER_PORT 6000
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE 10
struct Data //資料包
{
int length;
char receivemessage[2000]; //內容資訊
int fin;
};
int main() // (int argc, char* argv[])
{
struct sockaddr_in server_addr;
int server_socket;
int opt = 1;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(SERVER_PORT);
// 創建一個Socket
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0)
{
printf("Create Socket Failed!\n");
exit(1);
}
// bind a socket
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if(bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))
{
printf("Server Bind Port: %d Failed!\n", SERVER_PORT);
exit(1);
}
// 監聽Socket
if (listen(server_socket, 5))
{
printf("Server Listen Failed!\n");
exit(1);
}
struct sockaddr_in client_addr;
int client_socket;
socklen_t length;
// 連接客戶端Socket
length = sizeof(client_addr);
client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);
if (client_socket < 0)
{
printf("Server Accept Failed!\n");
}
FILE * fp;
if (!(fp = fopen("abc.bmp", "wb+")))
{
printf("open abc.bmp error");
exit(0);
}
Data data;
memset(&data, 0, sizeof(Data));
// 從客戶端接收資料
while(!data.fin)
{
memset(data.receivemessage, 0, sizeof(data.receivemessage));
length = recv(client_socket, (char *)&data, sizeof(Data), 0);
printf("size %d fin %d length %d\n",length,(int)data.fin,(int)data.length);
if (length < 0)
{
printf("Server Recieve Data Failed!\n");
break;
}
fwrite(data.receivemessage, data.length, 1, fp);
}
close(client_socket);
close(server_socket);
return 0;
}
window client: (send)
#include <stdio.h>
#include <Windows.h>
#pragma comment(lib, "ws2_32.lib")
#define Port 6000
#define IP_ADDRESS "10.14.105.88"
typedef struct Data
{
int length;
char sendMessage[2000];
int fin;
};
int main(int argc, char* argv[])
{
Data data;
WSADATA s;
SOCKET ClientSocket;
struct sockaddr_in ClientAddr;
int ret = 0;
char SendBuffer[MAX_PATH];
if (WSAStartup(MAKEWORD(2, 2), &s) != 0)
{
printf("Init Windows Socket Failed! Error: %d\n", GetLastError());
getchar();
return -1;
}
ClientSocket = socket(AF_INET,
SOCK_STREAM,
IPPROTO_TCP);
if (ClientSocket == INVALID_SOCKET)
{
printf("Create Socket Failed! Error: %d\n", GetLastError());
getchar();
return -1;
}
ClientAddr.sin_family = AF_INET;
ClientAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);
ClientAddr.sin_port = htons(Port);
memset(ClientAddr.sin_zero, 0X00, 8);
FILE* fp;
errno_t er = fopen_s(&fp, "./40000.bmp", "rb+");
fseek(fp, 0, SEEK_END);
int end = ftell(fp);
fseek(fp, 0, 0);
// 連接Socket
ret = connect(ClientSocket, (struct sockaddr*)&ClientAddr,sizeof(ClientAddr));
if (ret == SOCKET_ERROR)
{
printf("Socket Connect Failed! Error:%d\n", GetLastError());
getchar();
return -1;
}
else
{
printf("Socket Connect Succeed!");
}
while (end > 0)
{
printf("end %d \n", end);
memset(data.sendMessage, 0, sizeof(data.sendMessage));
fread(data.sendMessage, 1024, 1, fp);
if (end >= 1024)
{
data.fin = 0;
data.length = 1024;
}
else
{
data.fin = 1;
data.length = end;
}
ret = send(ClientSocket, (char*)&data, sizeof(Data), 0);
if (ret == SOCKET_ERROR)
{
printf("end %d \n", end);
printf("send error:%d\n", WSAGetLastError());
//printf("send() failed!\n");
break;
}
else
{
end -= 1024;
}
}
// 關閉socket
closesocket(ClientSocket);
WSACleanup();
getchar();
return 0;
}
uj5u.com熱心網友回復:
length越界了。接收的時候,拆包是否正確處理資料uj5u.com熱心網友回復:
不知道有多少前人掉在TCP Socketsend(人多)send(病少)send(財富)
recv(人多病)recv(少財富)
陷阱里面啊!
http://bbs.csdn.net/topics/380167545
uj5u.com熱心網友回復:

可以看看我初學時寫的傳檔案。
http://download.csdn.net/download/smwhotjay/2703081
我找到很多bug.
1. ret = send(ClientSocket, (char*)&data, sizeof 雖然資料填充的都是完整的。但是,send不一定就1次發送完。所以會導致接收端出錯。
比如你這個send data 是1024 的檔案資料,但是send
if (ret == SOCKET_ERROR)
{
printf("end %d \n", end);
printf("send error:%d\n", WSAGetLastError());
//printf("send() failed!\n");
break;
}
else
{
end -= 1024;
}
想當然的就認為1次發送完了整個結構體。所以就出現了你截圖的那幾個例外資料。 接收端顯示recv資料912個位元組。data.length顯示1024,這個是
if (end >= 1024)
{
data.fin = 0;
data.length = 1024;
}
client發送的。 而實際接收到的資料位元組肯定少于1024,因為client發送端沒發完。
2. send data 實際傳輸和位元組統計不一致導致bug . end -= 1024; 所以檔案塊會不完整,偶爾接收的檔案塊有殘缺
再就是結構體定義的話,如果是變長的,
int length;
char receivemessage[2000]; //內容資訊
int fin;
最好變長的資料變數放最后。越重要的資料優先放最前
int length;
int fin;
char receivemessage[2000];
uj5u.com熱心網友回復:
1、建議接收到的資料長度以recv到的長度為準2、 發送的資料長度以send為準
uj5u.com熱心網友回復:
是的,問題就在于接收的時候不完全,導致了length讀取到了一個很大的數字,在做fwrite的時候指標越界了,然后服務器就關閉了。
但是不懂得如何去做修改呀……
還望指教一下。
recv端是LINUX的,與windows不太一樣,我這邊windows to windows是能夠成功接收檔案的
uj5u.com熱心網友回復:
是的,問題就在于接收的時候不完全,導致了length讀取到了一個很大的數字,在做fwrite的時候指標越界了,然后服務器就關閉了。
但是不懂得如何去做修改呀……
還望指教一下。
recv端是LINUX的,與windows不太一樣,我這邊windows to windows是能夠成功接收檔案的
uj5u.com熱心網友回復:
data.length = fread(data.sendMessage, 1, 1024, fp); //按位元組讀取1024個單元data.fin = feof(fp) || data.length < 1024; //結束標記?
uj5u.com熱心網友回復:
要自己制定通信協議在接收端對資料進行重組
uj5u.com熱心網友回復:
tcp是基于流的,所以必須自行處理分包,常規做法是將資料長度和校驗碼放在包頭中
如果接收資料長度大于包頭,則先決議包頭,
校驗碼能通過驗證,則獲取后續資料的長度,
根據長度來判定接收是否完整。
uj5u.com熱心網友回復:
先資料處理再發送uj5u.com熱心網友回復:
明顯是基本功課沒做好fread要判斷回傳值,不能想當然以為小于1024就是結束 feof來判斷
send也一樣,判斷回傳值,不能想當然
結構定義也不對
struct Data //資料包
{
int length;
char receivemessage[2000]; //內容資訊
int fin;
};
應該改成
struct Data //資料包
{
__int32 length;
__int32 fin;
char receivemessage[2000]; //內容資訊
};
memset 之類的是多余的
發送的時候,要分開2個__int32 和后面的緩沖,要分開發送,并判斷回傳值
接收的時候要分開
先接受2個__int32
然后接識訓沖
萬一你上一個正好是1024,然后最后一個是1個位元組或者0位元組呢
代碼里太多的地方想當然了
uj5u.com熱心網友回復:
邊界條件是碼農永遠的痛!
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/82921.html
標籤:網絡編程
