TCP - поток байтов. send()
и recv()
могут вернуть меньше байтов, чем запрошено, поэтому вам необходимо вызывать их в циклах, чтобы гарантировать отправку / получение всех ожидаемых байтов.
Кроме того, вам нужно оформить свои сообщения таким образом, чтобы вы знали, где заканчивается сообщение и начинается следующее сообщение.
Кроме того, целые числа всегда должны передаваться с использованием целочисленных типов фиксированной ширины, а многобайтовые целые числа всегда должны передаваться в сетевом порядке байтов (с прямым порядком байтов) для согласованности через границы платформы.
Попробуйте что-то вроде этого:
Сервер
#include <iostream>
#include <string>
#include <limits>
#include <cstdint>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
bool filesize(const std::string &fname, std::uint32_t &fsize)
{
FILE *f = fopen(fname.c_str(), "rb");
if (!f)
{
std::cout << "Failed opening file\n";
return false;
}
if (fseek(f, 0, SEEK_END) != 0)
{
std::cout << "Failed seeking file\n";
fclose(f);
return false;
}
long len = ftell(f);
fclose(f);
if (len == -1L)
{
std::cout << "Failed getting file size\n";
return false;
}
if (sizeof(long) > sizeof(std::uint32_t))
{
if (len > std::numeric_limits<std::uint32_t>::max())
{
std::cout << "File size exceeds uint32_t max\n";
return false;
}
}
fsize = static_cast<std::uint32_t>(len);
return true;
}
int readAll(int sock, void *buffer, int buflen)
{
char *ptr = static_cast<char*>(buffer);
int x;
while (buflen > 0)
{
x = recv(sock, ptr, buflen, 0);
if (x <= 0)
{
if (x == 0)
std::cout << "Client disconnected\n";
else
std::cout << "Failed reading socket, error " << errno << "\n";
return x;
}
ptr += x;
buflen -= x;
}
return 1;
}
int readUInt32(int sock, std::uint32_t &value)
{
int x = readAll(sock, &value, sizeof(value));
if (x <= 0) return x;
value = ntohl(value);
return 1;
}
int readString(int sock, std::string &s)
{
s.clear();
// approach 1: null-terminated string
char buffer[1024];
int x, offset = 0;
do
{
x = readAll(sock, &buffer[offset], 1);
if (x <= 0)
return x;
if (buffer[offset] == '\0')
break;
if (++offset == sizeof(buffer))
{
s.append(buffer, offset);
offset = 0;
}
}
while (true);
if (offset > 0)
s.append(buffer, offset);
return 1;
// approach 2: length-prefixed string
std::uint32_t size;
int x = readUInt32(sock, size);
if ((x > 0) && (size > 0))
{
s.resize(size);
x = readAll(sock, &s[0], size);
}
if (x <= 0)
return x;
return 1;
}
bool sendAll(int sock, const void *buffer, int buflen)
{
const char *ptr = static_cast<const char*>(buffer);
int x;
while (buflen > 0)
{
x = send(sock, ptr, buflen, 0);
if (x < 0)
{
std::cout << "Failed sending socket, error " << errno << "\n";
return false;
}
ptr += x;
buflen -= x;
}
return true;
}
bool sendBool(int sock, bool value)
(
std::uint8_t temp = value;
return sendAll(sock, &temp, sizeof(temp));
}
bool sendUInt32(int sock, std::uint32_t value)
{
value = htonl(value);
return sendAll(sock, &value, sizeof(value));
}
int main()
{
int sockid = socket(AF_INET, SOCK_STREAM, 0);
if (sockid < 0)
{
std::cout << "Failed creating socket, error " << errno << "\n";
return 0;
}
int client_socket;
struct sockaddr_in server, client;
socklen_t client_size;
server.sin_family = AF_INET;
server.sin_port = htons(8791);
server.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t client_size;
if (bind(sockid, (struct sockaddr*)&server, sizeof(server)) < 0)
{
std::cout << "Failed binding socket, error " << errno << "\n";
close(sockid);
return 0;
}
std::cout << "Binded";
if (listen(sockid, 4) < 0)
{
std::cout << "Failed listening socket, error " << errno << "\n";
close(sockid);
return 0;
}
std::cout << "Listening....\n";
client_size = sizeof(client);
if ((client_socket = accept(sockid, (struct sockaddr*)&client, &client_size)) < 0)
{
std::cout << "Failed accepting client socket\n";
close(sockid);
return 0;
}
std::cout << "Client connected\n";
std::string fname;
if (readString(client_socket, fname) <= 0)
{
close(client_socket);
close(sockid);
return 0;
}
std::cout << fname << "\n";
std::uint32_t fsize;
bool success = filesize(fname, fsize);
if (!sendBool(client_socket, success))
{
std::cout << "Failed sending file size reply\n";
}
else if (success)
{
std::cout << "FILE SIZE : " << fsize << std::endl;
if (!sendUInt32(client_socket, fsize))
std::cout << "Failed sending file size\n";
}
close(client_socket);
close(sockid);
return 0;
}
Клиент
#include <iostream>
#include <string>
#include <cstdint>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
int readAll(int sock, void *buffer, int buflen)
{
char *ptr = static_cast<char*>(buffer);
int x;
while (buflen > 0)
{
x = recv(sock, ptr, buflen, 0);
if (x <= 0)
{
if (x == 0)
std::cout << "Server disconnected\n";
else
std::cout << "Failed reading socket, error " << errno << "\n";
return x;
}
ptr += x;
buflen -= x;
}
return 1;
}
int readBool(int sock, bool &value)
{
std::uint8_t temp;
int x = readAll(sock, &temp, sizeof(temp));
if (x <= 0) return x;
value = (temp != 0);
return 1;
}
int readUInt32(int sock, std::uint32_t &value)
{
int x = readAll(sock, &value, sizeof(value));
if (x <= 0) return x;
value = ntohl(value);
return 1;
}
bool sendAll(int sock, const void *buffer, int buflen)
{
const char *ptr = static_cast<const char*>(buffer);
int x;
while (buflen > 0)
{
x = send(sock, ptr, buflen, 0);
if (x < 0)
{
std::cout << "Failed sending socket, error " << errno << "\n";
return false;
}
ptr += x;
buflen -= x;
}
return true;
}
bool sendString(int sock, const std::string &s)
{
// approach 1: null-terminated string
return sendAll(sock, s.c_str(), s.size()+1);
// approach 2: length-prefixed string
std::uint32_t size = s.size();
return sendUInt32(sock, size) && sendAll(sock, s.c_str(), size);
}
int main()
{
int sockid = socket(AF_INET, SOCK_STREAM, 0);
if (sockid < 0)
{
std::cout << "Failed creating socket, error " << errno << "\n";
return 0;
}
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(8797);
server.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(sockid, (struct sockaddr*)&server, sizeof(server)) < 0)
{
std::cout << "Failed connecting socket, error " << errno << "\n";
close(sockid);
return 0;
}
std::cout << "Connected\n";
std::cout << "Enter File name : ";
std::string fname;
std::getline(std::cin, fname);
if (!sendString(sockid, fname))
{
std::cout << "Failed sending file name\n";
close(sockid);
return 0;
}
bool success;
if (readBool(sockid, success) <= 0)
{
std::cout << "Failed receiving file size reply\n";
close(sockid);
return 0;
}
if (success)
{
std::uint32_t fsize;
if (readUInt32(sockid, fsize) <= 0)
{
std::cout << "Failed receiving file size\n";
close(sockid);
return 0;
}
std::cout << "FILE-SIZE : " << fsize << "\n";
}
else
std::cout << "FILE-SIZE error\n";
close(sockid);
return 0;
}