我目前正在为iOS编写客户端 - 服务器应用程序。客户端是Objective-C / CocoaTouch,服务器后端是用C ++编写的。现在,我正在努力解决TCP通信问题 - 将文件发送到服务器,具体而言。
每个其他数据包发送/接收都有效,但对于此数据包,服务器似乎收到“坏”数据。服务器和客户端都是小端,因此字节顺序不是问题。
这是发件人代码:
- (void) sendGameUpdateWithFile:(NSString*)filePath gameID:(NSInteger)gameID {
NSMutableData* data = [[NSMutableData alloc] init];
data = [NSMutableData dataWithContentsOfFile:filePath];
fileheadPacket head;
head.msgtype = 0x12;
strncpy(head.data1, [myUsername cStringUsingEncoding:NSUTF8StringEncoding], [myUsername length]);
head.data1[[myUsername length]] = '\0';
int followingPackets = 1;
if([data length] % 1024 == 0){
if([data length] > 1024){
followingPackets = (int)[data length]/1024;
}
} else {
followingPackets = (int)([data length]/1024)+1;
}
head.following = followingPackets;
head.fileid = (int)gameID;
head.size = sizeof(fileheadPacket);
[mySock writeData:[NSData dataWithBytes:&head length:sizeof(fileheadPacket)] withTimeout:-1 tag:8];
NSRange thisRange;
thisRange.length = 1024;
for(int i = 0; i < followingPackets; i++){
thisRange.location = i*1024;
if(thisRange.location + thisRange.length > [data length]){
thisRange.length = [data length] - thisRange.location;
}
filePacket tmp;
tmp.extra = (int)thisRange.length;
tmp.msgtype = 0x13;
tmp.size = sizeof(filePacket);
tmp.following = (int)gameID;
[data getBytes:tmp.fileBuffer range:thisRange];
[mySock writeData:[NSData dataWithBytes:&tmp length:sizeof(filePacket)] withTimeout:-1 tag:8];
NSLog(@"Wrote packet of type 0x%02x, size %d", tmp.msgtype, tmp.size);
}
}
请原谅该代码的混乱,它仍处于早期开发阶段并将得到改进。
这是接收者的代码:
std::vector<filePacket> PacketInterpreter::readPacket(filePacket* inPacket, int FD){
//The returned vector contains stuff to return to the socket. If it's empty, there's nothing to return (and thus, the client on that FD shouldn't be listening)
packetsToSend.clear();
packetsToSend.reserve(1024);
std::cout << "Received a packet of type " << inPacket->msgtype << " with size " << inPacket->size << std::endl;
switch (inPacket->msgtype){
case 0x12:
{
fileheadPacket* ptr = (fileheadPacket*) inPacket;
assembleFile(ptr->following, ptr->fileid);
break;
}
case 0x13:
concatFilePart(inPacket->extra, inPacket->following, inPacket->fileBuffer);
break;
}
return packetsToSend;
}
void PacketInterpreter::assembleFile(int following, int gameID){
std::cout << "assembleFile with following: " << following << " and gameid " << gameID << std::endl;
char* s = (char*)malloc(sizeof(char)*(following+1)*1024);
inAssembly.insert(std::make_pair(gameID, s));
inAssemblyCountdown.insert(std::make_pair(gameID, following+1));
inAssemblyCountup.insert(std::make_pair(gameID, 0));
}
void PacketInterpreter::concatFilePart(int readLen, int gameID, unsigned char* data){
for(int i = 0; i < readLen; i++){
inAssembly[gameID][inAssemblyCountup[gameID]] = data[i];
inAssemblyCountup[gameID]++;
}
inAssemblyCountdown[gameID]--;
if(readLen != 1024 || inAssemblyCountdown[gameID] == 0){
makeFile(gameID, inAssembly[gameID]);
delete inAssembly[gameID];
inAssembly.erase(gameID);
inAssemblyCountdown.erase(gameID);
}
}
void PacketInterpreter::makeFile(int gameID, char* buffer){
std::string rmgameIDdir = ("rm -r " + std::to_string(gameID));
system(rmgameIDdir.c_str());
std::string gameIDdir = "mkdir " + std::to_string(gameID);
std::string constructedPath = std::to_string(gameID);
constructedPath += "/";
std::system(gameIDdir.c_str());
std::cout << gameIDdir.c_str() << std::endl;
std::ofstream thisFile;
thisFile.open(constructedPath+"sound.caf");
std::cout << "opened " << constructedPath+"sound.caf" << " is open? " << thisFile.is_open() << std::endl;
std::string temp = "";
for(int i = 0; i < inAssemblyCountup[gameID]; i++){
thisFile << std::hex << buffer[i];
}
thisFile.close();
std::cout << "File assembled at " << constructedPath << "sound.caf" << std::endl;
}
void PacketInterpreter::assembleFile(int following, int gameID){
std::cout << "assembleFile with following: " << following << " and gameid " << gameID << std::endl;
char* s = (char*)malloc(sizeof(char)*(following+1)*1024);
inAssembly.insert(std::make_pair(gameID, s));
inAssemblyCountdown.insert(std::make_pair(gameID, following+1));
inAssemblyCountup.insert(std::make_pair(gameID, 0));
}
void PacketInterpreter::concatFilePart(int readLen, int gameID, unsigned char* data){
for(int i = 0; i < readLen; i++){
inAssembly[gameID][inAssemblyCountup[gameID]] = data[i];
inAssemblyCountup[gameID]++;
}
inAssemblyCountdown[gameID]--;
if(readLen != 1024 || inAssemblyCountdown[gameID] == 0){
makeFile(gameID, inAssembly[gameID]);
delete inAssembly[gameID];
inAssembly.erase(gameID);
inAssemblyCountdown.erase(gameID);
}
}
我在连接两侧使用的结构如下所示:
//Packet used for small stuff
typedef struct small_packet {
int msgtype;
int size;
int extra;
int following;
char data1[64];
char data2[64];
} packet;
//Packet used for files
typedef struct file_packet {
int msgtype; //For partial file packet this should be 0x02
int size;
int extra;
int following;
char data1[64];
unsigned char fileBuffer[1024];
} filePacket;
//Used for file headers
typedef struct filehead_packet {
int msgtype;
int size;
int extra;
int following;
char data1[64];
int fileid;
char rest[60];
} fileheadPacket;
我确信结构在两个单元上都以相同的方式打包,因为所有其他消息都使用相同的数据包,并且它们可以正常工作。当我尝试从客户端 - >服务器发送文件时,这是一个典型的日志:
Received a packet of type 18 with size 144
assembleFile with following: 367 and gameid 109
Received a packet of type 19 with size 1104
Received a packet of type 19 with size 1104
Received a packet of type 19 with size 1104
Received a packet of type 19 with size 1104
Checking for new games
Received a packet of type 0 with size 0
Received a packet of type -262149 with size -196612
Received a packet of type -131075 with size -196612
Received a packet of type -1 with size -1
Received a packet of type 327685 with size 327685
Received a packet of type 131074 with size 196611
Received a packet of type 65537 with size 65537
Received a packet of type -65538 with size -65538
Received a packet of type 131074 with size 196611
Received a packet of type 196611 with size 65537
Received a packet of type 131074 with size 196611
Received a packet of type 393222 with size 196611
Received a packet of type 0 with size 0
... many packets which make no sense, although the total number of packets is correct (367 in this case) ...
Received a packet of type -196612 with size -196612
Received a packet of type 131074 with size 131074
Received a packet of type 65537 with size 131074
Received a packet of type -196612 with size -196612
Received a packet of type 0 with size 65537
Received a packet of type -393223 with size -327686
Received a packet of type 131074 with size 131074
Received a packet of type 393222 with size 393222
Received a packet of type 0 with size 0
Received a packet of type 19 with size 1104
Received a packet of type 19 with size 1104
Received a packet of type 19 with size 1104
mkdir 109
opened 109/sound.caf is open? 1
File assembled at 109/sound.caf
这让我很生气,因为我不知道出了什么问题。如果您需要更多代码示例(例如main()中的select() - 发送和接收数据包的块,请告诉我。非常感谢任何帮助。提前致谢!
答案 0 :(得分:-2)
我实际上发现了这里的错误。服务器读取的速度比iPhone发送数据包的速度快,这意味着当服务器尝试读取完整的sizeof(filePacket)时,只有从iPhone传输的数据包的前半部分到达服务器文件描述符的行尾。
由于两台机器都是小端,因此服务器首先读取数据包的“尾部”,在这种情况下,每个数据包中包含的元信息存储在当前可用数据的“末尾”。文件描述符。当我们尝试读取1140个字节并且只说...其中一半实际到达时,元数据从数据包的fileBuffer字段内部获取随机值。
我通过“减慢”服务器端的读取来快速解决问题,但我认为我必须减少filePacket结构的总大小,以使其正常运行而不会以编程方式减慢。
那我今天学到了什么?不要通过TCP发送太大的数据包,并期望服务器在一个块中读取所有数据包。一次发送1K太多了。