I'm writing a client server udp application in c++. The application works correctly when the client and server are running on the same machine, however when I run my client on my laptop and the server on an ec2 instance in AWS, the server receives 4 messages and then begins to hang on recvfrom. The client is sending a file in chunks of 500 bytes and the server is writing these to a file. The server sends an ack after it receives each chunk. The client has a timeout of 2 seconds, if it does not receive the ack before the timeout it resends the previous chunk. Here is my client code
void Sender::send_file(const char* host) {
cout << "in send file\n";
int sock = 0;
struct sockaddr_in serv_addr;
size_t length = 8;
unsigned char res_buf[8] = {0};
if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
std::cout << "\n Socket creation error \n";
exit(EXIT_FAILURE);
}
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
// copy 0 into serv_addr members
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = inet_addr(host);
size_t file_size = buffer.size();
cout << "file size " << to_string(file_size) << "\n";
size_t file_pos = 0;
int next_iteration = 1;
bool establish = true;
while(file_pos < file_size - 1) {
cout << "waiting for lock\n";
std::unique_lock<std::mutex> lock(mrun);
main_ready.wait(lock, [&next_iteration, this]{return next_iteration == current_iteration; });
lock.unlock();
cout << "acquired lock\n";
++next_iteration;
Segment segment;
vector<unsigned char> req_str;
if(!establish) {
if(file_pos + mss < file_size) {
std::vector<unsigned char> file_chunk(buffer.begin() + file_pos, buffer.begin() + file_pos + mss);
segment.data = file_chunk;
file_pos += mss;
}
else {
std::vector<unsigned char> file_chunk(buffer.begin() + file_pos, buffer.end());
segment.data = file_chunk;
file_pos = file_size;
}
segment.seq_num = file_pos - mss;
req_str = segment.to_bytes(false, mss);
}
else {
segment.seq_num = 0;
req_str = segment.to_bytes(true, mss);
}
bool is_ack = false, timed_out = false;
std::chrono::high_resolution_clock::time_point start_time, end_time;
long duration = 0;
unsigned char* req = req_str.data();
size_t num_bytes = req_str.size();
unsigned int len = 0;
while(!is_ack) {
cout << "about to send data " << std::to_string(segment.seq_num) << " bytes " << std::to_string(num_bytes) << "\n";
ssize_t send_res = sendto(sock, req, num_bytes, 0, (const struct sockaddr *) &serv_addr, sizeof(serv_addr));
printf("Sent to %s:%d\n", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
cout << "send res " << to_string(send_res) << " " << strerror(errno) << "\n";
start_time = std::chrono::high_resolution_clock::now();
bzero(res_buf, length);
cout << "about to read ack\n";
ssize_t block_sz = recvfrom(sock, res_buf, length, 0, (struct sockaddr *) &serv_addr, &len);
cout << "read ack\n";
end_time = std::chrono::high_resolution_clock::now();
duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
cout << "block sz " << std::to_string(block_sz) << " duration " << std::to_string(duration) << std::endl;
if(duration > timeout * 1000)
timed_out = true;
if(timed_out || errno == ETIMEDOUT || block_sz < 0) {
std::cout << "time out/error occurred on read " << to_string(errno) << "\n";
cout << strerror(errno) << "\n";
bzero(res_buf, length);
timed_out = false;
continue;
}
//add_nulls(res_buf);
is_ack = read_response(segment.seq_num, res_buf);
cout << "is ack " << is_ack << "\n\n";
bzero(res_buf, length);
}
establish = false;
lock.lock();
if(--worker_count == 0) {
lock.unlock();
iteration_complete.notify_one();
}
}
cout << "Out of while loop\n";
close(sock);
files_sent.push_back(1);
}
It's sending exactly 508 bytes every time, except for the 1st segment which is only 10 bytes. I print the results of sendto after every iteration which always looks like this
Sent to 52.0.0.0:7735
send res 508 Undefined error: 0
* **Update ***
Once the server begins to hang in recvfrom and the client times out waiting for the ack, the client begins to print the following error continuously as it keeps resending the chunk.
Sent to 52.0.0.0:7735
send res 508 Resource temporarily unavailable
The server code looks like
void Receiver::download_file() {
int server_fd;
struct sockaddr_in serv_addr, cli_addr;
int segment_size = 20;
umap seg_map;
ACK_Segment::init_static();
std::random_device rd; //Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
std::uniform_real_distribution<> dis(0.0, 1.0);
if((server_fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
memset(&serv_addr, 0, sizeof(serv_addr));
memset(&cli_addr, 0, sizeof(cli_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(PORT);
// bind socket to port
//bzero(buffer, segment_size);
ssize_t block_sz = 0;
unsigned int len = sizeof cli_addr;
bool is_set_mss = true;
if(bind(server_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
//unsigned char* buffer = bvec.data();
vector<unsigned char> bvec(segment_size);
unsigned char* buffer = bvec.data();
bool is_resized = false;
while(true) {
cout << "about to recvfrom\n";
printf("Receiving from %s:%d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
cout << "segment size " << to_string(segment_size) << " len " << to_string(len) << "\n";
block_sz = recvfrom(server_fd, buffer, segment_size, 0, ( struct sockaddr *) &cli_addr, &len);
cout << "block size " << to_string(block_sz) << "errno " << to_string(errno) << " " << strerror(errno) << "\n";
if(block_sz > 0) {
cout << "Received data - bytes " << std::to_string(block_sz) << "\n";
printf("Received from %s:%d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
double rand_val = dis(gen);
if(is_set_mss) {
seg_map = read_segment(bvec, block_sz, true);
if(seg_map["checksum_valid"] == "true") {
segment_size = mss + HEADER_SIZE;
is_resized = true;
}
}
else {
if(block_sz == 0) {
cout << "received no data\n";
break;
}
seg_map = read_segment(bvec, block_sz, false);
}
if(seg_map["checksum_valid"] == "false")
cout << "invalid checksum\n";
else if(is_set_mss) {
vector<unsigned char> ack = get_ack();
//remove_nulls(ack);
unsigned char* ack_bytes = ack.data();
cout << "about to send ack\n";
sendto(server_fd, ack_bytes, 8, 0, (const struct sockaddr *) &cli_addr, len);
//send(new_socket, ack, 8, 0);
is_set_mss = false;
}
else {
if(seg_map["in_order"] == "true") {
string data = seg_map["data"];
std::ofstream out;
out.open(file_name, std::ios_base::app);
out << data;
out.close();
cout << "Done writing file chunk\n";
}
vector<unsigned char> ack = get_ack();
unsigned char* ack_bytes = ack.data();
sendto(server_fd, ack_bytes, 8, 0, (const struct sockaddr *) &cli_addr, len);
}
if(is_resized) {
bvec.resize(segment_size);
is_resized = false;
}
bvec.clear();
buffer = bvec.data();
cout << "Cleared buffer\n\n";
}
cout << "out of inner while\n";
}
}
This prints
Receiving from 168.0.0.0:18832
segment size 508 len 16
block size 508 errno 0 Success
However it only prints this 4 times, then it hangs on recvfrom while the client continues to send 508 bytes, and the segment size remains at 508 in recvfrom. Why would recvfrom start out working and then all of the sudden stop?
Update
I watered this down to the simplest udp client server I could write, here is the client:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <string>
using std::vector;
using std::cout;
using std::to_string;
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
size_t length = 11;
unsigned char res_buf[11] = {0};
if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
std::cout << "\n Socket creation error \n";
exit(EXIT_FAILURE);
}
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
// copy 0 into serv_addr members
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(7735);
serv_addr.sin_addr.s_addr = inet_addr("52.71.240.175");
vector<unsigned char> req(20);
for(int i = 0; i < 20; i++)
req[i] = 'A';
unsigned char* req_str = req.data();
size_t num_bytes = req.size();
unsigned int len = sizeof serv_addr;
while(true) {
cout << "about to send data\n";
ssize_t send_res = sendto(sock, req_str, num_bytes, 0, (const struct sockaddr *) &serv_addr, sizeof(serv_addr));
bzero(res_buf, length);
cout << "about to read ack\n";
ssize_t block_sz = recvfrom(sock, res_buf, length, 0, (struct sockaddr *) &serv_addr, &len);
cout << "received " << res_buf << "\n";
}
}
And here is the server:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <string>
using std::vector;
using std::cout;
using std::to_string;
int main() {
int server_fd;
struct sockaddr_in serv_addr, cli_addr;
int segment_size = 20;
memset(&serv_addr, 0, sizeof(serv_addr));
memset(&cli_addr, 0, sizeof(cli_addr));
if((server_fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(7735);
unsigned int len = sizeof cli_addr;
if(bind(server_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
vector<unsigned char> bvec(segment_size);
unsigned char* buffer = bvec.data();
while(true) {
cout << "about to receive from\n";
ssize_t block_sz = recvfrom(server_fd, buffer, segment_size, 0, ( struct sockaddr *) &cli_addr, &len);
if(block_sz > 0) {
printf("Received from %s:%d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
char* ack_bytes = "ack message";
cout << "about to send ack\n";
sendto(server_fd, ack_bytes, 11, 0, (const struct sockaddr *) &cli_addr, len);
}
}
}
And the same behavior happens, the server receives 4 messages, but then on the fifth message it just hangs in recvfrom. I've tried closing and reopening the socket in each iteration on the server, that had no effect.
I had to close and reopen the socket on the client side after receiving each ack from the server. I didn't need to close and reopen on the server side. I'm not sure if this addresses the root cause of the problem I was facing but it allows my application to run correctly.
Related
I am trying to establish two sockets that are both able to multicast transmit. And the should both be members of each others multicast group.
I have two problems.
I have tried to disable the loopback, so that a transmitter does not receive its own messages. This does not seem to work.
The two transmitters should be the same, but only one of them receives the message sent on their multicast group.
The code looks like this:
#define ERROR_CODE -1
int init(const char *rx, const char *tx, uint16_t port, struct sockaddr_in& txaddress)
{
int status = 0;
int enable = 1;
int disable = 0;
int mainSocket = ::socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, 0);
if (mainSocket == ERROR_CODE)
std::cout << "Error creating socket." << std::endl;
if (::setsockopt(mainSocket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) == ERROR_CODE)
std::cout << "System failed to set reuse address." << std::endl;
if (::setsockopt(mainSocket, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable)) == ERROR_CODE)
std::cout << "System failed to set reuse port." << std::endl;
status = setsockopt(mainSocket, IPPROTO_IP, IP_MULTICAST_LOOP, &disable, sizeof(disable));
if (status == ERROR_CODE)
std::cout << "Unable to disable multicast loopback." << std::endl;
struct sockaddr_in rxaddress = address(INADDR_ANY, port);
txaddress = address(inet_addr(tx), port);
status = ::bind(mainSocket, reinterpret_cast<sockaddr*>(&rxaddress), sizeof(sockaddr_in));
if (status == ERROR_CODE)
std::cout << "Error binding socket." << std::endl;
struct in_addr localInterface;
memset(&localInterface, 0, sizeof(localInterface));
localInterface.s_addr = inet_addr("127.0.0.1");
status = setsockopt(mainSocket, IPPROTO_IP, IP_MULTICAST_IF, (char*) &localInterface, sizeof(localInterface));
if (status == ERROR_CODE)
std::cout << "Unable to set interface to multicast." << std::endl;
struct ip_mreq group;
memset(&group, 0, sizeof(group));
group.imr_multiaddr.s_addr = inet_addr(rx);
group.imr_interface.s_addr = INADDR_ANY;
status = setsockopt(mainSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &group, sizeof(group));
if (status == ERROR_CODE)
std::cout << "Unable to add to multicast group." << std::endl;
return mainSocket;
}
Where
struct sockaddr_in address(uint32_t ip, uint16_t port)
{
struct sockaddr_in address;
memset(&address, 0, sizeof(sockaddr_in));
address.sin_family = AF_INET;
if (ip == INADDR_BROADCAST)
address.sin_addr.s_addr = htonl(INADDR_BROADCAST);
else
if (ip == INADDR_ANY)
address.sin_addr.s_addr = htonl(INADDR_ANY);
else
address.sin_addr.s_addr = ip;
address.sin_port = htons(port);
return address;
}
And the testcode is
#include <iostream>
#include <arpa/inet.h>
#include <cstring>
#include <thread>
#define MULTICAST_SERVER_ADDRESS "224.0.0.1"
#define MULTICAST_CLIENT_ADDRESS "224.0.0.0"
int main (int argc, char** argv)
{
char message1[16] = "server says hi.";
char message2[16] = "client says hi.";
char buffer1[100] = {0};
char buffer2[100] = {0};
socklen_t length = sizeof(sockaddr_in);
struct sockaddr_in servertxaddress;
struct sockaddr_in clienttxaddress;
memset(&servertxaddress, 0, length);
memset(&clienttxaddress, 0, length);
int serversocket = init(MULTICAST_CLIENT_ADDRESS, MULTICAST_SERVER_ADDRESS, 2000, servertxaddress);
int clientsocket = init(MULTICAST_SERVER_ADDRESS, MULTICAST_CLIENT_ADDRESS, 2000, clienttxaddress);
sendto(serversocket, message1, 16, 0, (const sockaddr*) &servertxaddress, length);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
recvfrom(clientsocket, buffer1, 16, 0, nullptr, nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << buffer1 << std::endl;
sendto(clientsocket, message2, 16, 0, (const sockaddr*) &clienttxaddress, length);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
recvfrom(serversocket, buffer2, 160, 0, nullptr, nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << buffer2 << std::endl;
return 0;
}
When I run the program, both sockets receive the multicast message (message1), so the IP_MULTICAST_LOOP I tried does not work, as I would expect it to. And the last transmit seems to get transmitted, but it does not arrive in the other end.
So the output is:
server says hi.
server says hi.
And I would like it to be:
server says hi.
client says hi.
Please advise.
I'm trying to create an app which would accept many connections from clients at the same time and it works for me, but it also should download those files at the same time. In this version of server, even if clients are connected simultaneously, files are written one by one.
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <ctime>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wsaData;
int winsock_result = WSAStartup(MAKEWORD(2,2), &wsaData);
if(winsock_result != 0)
{
exit(1);
}
SOCKET server_socket;
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(server_socket == INVALID_SOCKET)
{
WSACleanup();
exit(1);
}
int const max_clients = 100;
int client_socket[max_clients];
for (int i = 0; i < max_clients; i++)
{
client_socket[i] = 0;
}
char* ip_address = "127.0.0.1";
int port = 6666;
SOCKADDR_IN server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip_address);
int server_sizeof = sizeof(server);
int opt = TRUE;
if( setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
{
closesocket(server_socket);
WSACleanup();
exit(1);
}
if(bind(server_socket,(SOCKADDR *)&server, server_sizeof) == SOCKET_ERROR)
{
closesocket(server_socket);
WSACleanup();
exit(1);
}
if(listen(server_socket, 5) == SOCKET_ERROR)
{
std::cout << "Nasluchiwanie portu nieudane." << std::endl;
}
else
{
std::cout << "Nasluchiwanie portu " << port << " udane." << std::endl << std::endl;
}
int const buffer_size = 512;
char buffer[buffer_size];
int max_socket_descriptor, socket_descriptor;
int downloaded_files = 1;
fd_set readfds;
while(true)
{
FD_ZERO(&readfds);
FD_SET(server_socket, &readfds);
max_socket_descriptor = server_socket;
for (int i = 0 ; i < max_clients ; i++)
{
socket_descriptor = client_socket[i];
if(socket_descriptor > 0)
{
FD_SET( socket_descriptor, &readfds);
}
if(socket_descriptor > max_socket_descriptor)
{
max_socket_descriptor = socket_descriptor;
}
}
if ((select( max_socket_descriptor + 1, &readfds, NULL, NULL, NULL) < 0) && (errno != EINTR))
{
std::cout << "Blad funkcji select." << std::endl;
}
if (FD_ISSET(server_socket, &readfds))
{
int new_sockfd;
if ((new_sockfd = accept(server_socket,(SOCKADDR *)&server, &server_sizeof)) == SOCKET_ERROR)
{
std::cout << "Otrzymanie deskryptora nieudane." << std::endl;
}
else
{
for (int i = 0; i < max_clients; i++)
{
if( client_socket[i] == 0 )
{
client_socket[i] = new_sockfd;
std::cout << "Dodawanie do listy socketow jako numer " << i << std::endl;
break;
}
}
}
}
for (int i = 0; i < max_clients; i++)
{
socket_descriptor = client_socket[i];
if (FD_ISSET( socket_descriptor, &readfds))
{
struct sockaddr_in client_address;
char filename[buffer_size];
std::stringstream ip_filename;
ip_filename << "plik" << downloaded_files << "_" << inet_ntoa(client_address.sin_addr);
strcpy(filename, ip_filename.str().c_str());
std::cout << "Nazwa pliku (IP klienta): " << filename << std::endl;
FILE* file;
file = fopen(filename, "wb");
const clock_t begin_time = clock();
int received_size;
do
{
memset(buffer, 0, buffer_size);
received_size = recv(socket_descriptor, buffer, buffer_size, 0);
if (received_size == 0 || received_size == -1)
{
break;
}
fwrite(buffer, sizeof(char), received_size, file);
}
while (received_size != 0);
fclose(file);
std::cout << "Czas wysylania pliku: " << float( clock () - begin_time ) / CLOCKS_PER_SEC << " sekund." << std::endl << std::endl;
closesocket(socket_descriptor);
client_socket[i] = 0;
downloaded_files++;
}
}
}
closesocket(server_socket);
WSACleanup();
system("pause");
return 0;
}
What should I do to make them write many at the same time? I've tried many modifications of the code above but every time I can't get wanted result.
For example:
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <ctime>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wsaData;
int winsock_result = WSAStartup(MAKEWORD(2,2), &wsaData);
if(winsock_result != 0)
{
exit(1);
}
SOCKET server_socket;
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(server_socket == INVALID_SOCKET)
{
WSACleanup();
exit(1);
}
int const max_clients = 100;
int client_socket[max_clients];
for (int i = 0; i < max_clients; i++)
{
client_socket[i] = 0;
}
char* ip_address = "127.0.0.1";
int port = 6666;
SOCKADDR_IN server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip_address);
int server_sizeof = sizeof(server);
int opt = TRUE;
if( setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
{
closesocket(server_socket);
WSACleanup();
exit(1);
}
if(bind(server_socket,(SOCKADDR *)&server, server_sizeof) == SOCKET_ERROR)
{
closesocket(server_socket);
WSACleanup();
exit(1);
}
if(listen(server_socket, 5) == SOCKET_ERROR)
{
std::cout << "Nasluchiwanie portu nieudane." << std::endl;
}
else
{
std::cout << "Nasluchiwanie portu " << port << " udane." << std::endl << std::endl;
}
int const buffer_size = 512;
char buffer[buffer_size];
int max_socket_descriptor;
int downloaded_files = 1;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(server_socket, &readfds);
max_socket_descriptor = server_socket;
while(true)
{
if ((select( max_socket_descriptor + 1, &readfds, NULL, NULL, NULL) < 0) && (errno != EINTR))
{
std::cout << "Blad funkcji select." << std::endl;
}
for (int i = 0 ; i < max_clients ; i++)
{
if(FD_ISSET(server_socket, &readfds))
{
int new_sockfd;
if ((new_sockfd = accept(server_socket,(SOCKADDR *)&server, &server_sizeof)) == SOCKET_ERROR)
{
std::cout << "Otrzymanie deskryptora nieudane." << std::endl;
}
else
{
for (int i = 0; i < max_clients; i++)
{
if( client_socket[i] == 0 )
{
client_socket[i] = new_sockfd;
FD_SET( client_socket[i], &readfds);
if(client_socket[i] > max_socket_descriptor)
{
max_socket_descriptor = client_socket[i];
}
std::cout << "Dodawanie do listy socketow jako numer " << i << std::endl;
break;
}
}
}
}
if(FD_ISSET(client_socket[i], &readfds))
{
struct sockaddr_in client_address;
char filename[buffer_size];
std::stringstream ip_filename;
ip_filename << "plik" << downloaded_files << "_" << inet_ntoa(client_address.sin_addr);
strcpy(filename, ip_filename.str().c_str());
std::cout << "Nazwa pliku (IP klienta): " << filename << std::endl;
FILE* file;
memset(buffer, 0, buffer_size);
int received_size;
received_size = recv(client_socket[i], buffer, buffer_size, 0);
if (received_size <= 0)
{
closesocket(client_socket[i]);
FD_CLR(client_socket[i], &readfds);
client_socket[i] = 0;
break;
}
else
{
file = fopen(filename, "ab");
fwrite(buffer, sizeof(char), received_size, file);
fclose(file);
}
downloaded_files++;
}
}
}
closesocket(server_socket);
WSACleanup();
system("pause");
return 0;
}
I thought about opening and closing those files every received packet and appending every packet to them, but I really don't have idea how to do it. The example of modified code was meant to do it, but it doesn't work.
I'm forbidden to use other processes and threads than the main one, so I'm kinda helpless now. Thanks for your help.
You have the basic loop with select in place, which is good.
accept is already (mostly) non-blocking. You just need to turn on non-blocking mode on the client sockets and then you'll be able to handle multiple client reads, writes and accepts in your main select loop.
You can have a vector of client-specific data per client, with each entry containing the client socket, the opened file and any other client-specific state.
After the accept, you create a new client entry and add it to the vector. Then in the main loop you do FD_SET for accept and all client's reads and writes. After the select, you inspect the the FD sets and handle them one by one. For best performance you will want your file I/O also in non-blocking mode, but for this assignment that's probably overkill.
For a benchmarking task, I'm creating multiple server workers with the same socket descriptor and client workers with separate connections.
Though client threads are sending a copy of the same message for each thread. The server shows it's received only one copy of each message, not all copies, sent by sending by all client threads.
for 4 threads and 2 messages {0,1} , client output
client 3Wrote 0
client 1Wrote 1
client 0Wrote 0
client 1Wrote 0
client 2Wrote 0
client 3Wrote 1
client 2Wrote 1
Where as server output
Server 0 Received - 0
Server 2 Received - 1
Why aren't rest of the messages showing up ? Or they were never actually sent by the client ?
void *clientWorker(void *threadarg) {
struct workDetails *thisWork;
thisWork = (struct workDetails *) threadarg;
int threadcount = thisWork->threadcount;
int chunkSize = thisWork->chunkSize;
char *serverIp = thisWork->serverIp;
char *dataStore = thisWork->dataStore;
int sockfd;
struct sockaddr_in serv_addr;
struct hostent *server;
char *buffer = (char *) calloc(chunkSize, sizeof(char));
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening client socket");
server = gethostbyname(serverIp);
if (server == NULL) error("Could not detect server by node name");
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *) server->h_addr,
(char *) &serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(PORTNO);
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("Server is not up.");
long totalPackets = oneGBtoByte / (chunkSize * threadcount);
long i;
for (i = 0; i < totalPackets; ++i) {
sprintf(buffer, "%ld", i);
if (write(sockfd, buffer, chunkSize) < 0)
error("ERROR writing to socket");
else {
lock_guard<mutex> guard(cout_mutex);
cout << " client " << thisWork->threadid << "Wrote " << i << endl;
}
}
cout << " client " << thisWork->threadid << "Exit";
pthread_exit((void *) NULL);
}
void *serverWorker(void *threadarg) {
struct workDetails *thisWork;
thisWork = (struct workDetails *) threadarg;
int threadcount = thisWork->threadcount;
int chunkSize = thisWork->chunkSize;
char *dataStore = thisWork->dataStore;
int sockfd = thisWork->sockfd;
char *buffer = new char[chunkSize];
while (1) {
if(read(sockfd, buffer, chunkSize) > 0){
lock_guard<mutex> guard(cout_mutex);
cout << "Server " << thisWork->threadid << " Received - " << buffer << endl;
}
}
pthread_exit((void *) NULL);
}
The problem is accept connection code was in the main thread(As not visible in the code). And hence server accepted only one connection though client tried to create 4 connection. And the out put did not reveal this issue as the code doesn't send an acknowledgment back or forth. (No ping pong . ideally most receive is followed by send ack, and every send is followed by recv ack).
When I moved that accept connection code to thread worker . It worked as intended.
void *serverWorker(void *threadarg) {
struct workDetails *thisWork;
thisWork = (struct workDetails *) threadarg;
int threadcount = thisWork->threadcount;
int chunkSize = thisWork->chunkSize;
char *dataStore = thisWork->dataStore;
int sockfd = thisWork->sockfd;
struct sockaddr_in cli_addr; // = thisWork->cli_addr;
char *buffer = new char[chunkSize];
listen(sockfd, 5);
socklen_t clilen = sizeof(cli_addr);
sockfd = ::accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (sockfd < 0)
error("ERROR on accept");
while (1) {
if (recv(sockfd, buffer, chunkSize, 0) > 0) {
//lock_guard<mutex> guard(cout_mutex);
//cout << "Server " << thisWork->threadid << " Received - " << buffer << endl;
buffer[chunkSize] = NULL;
strncat(dataStore, buffer, strlen(buffer));
if (strlen(dataStore) == oneGBtoByte) {
cout << "Done - " << endl;
storeInfile(created, dataStore);
break;
}
}
}
//End
delete[] buffer;
pthread_exit((void *) NULL);
}
How ever what ever #jeremy-friesner or #gil-hamilton told is true. But those issues are not important here , as I'm only interested in total byte received and not who received which chunk.
I want to make a server and client program with TCP protocol using C++. The server must be able to handle multiple client at once. But the problem is for example, after starting the server, I run 2 clients with the server 's IP address and port as parameters. Next, both clients are sending data to server. At first, both clients could send data to server and the server was able read the data. But, once the server has received data from the second client, it seems that it stopped receiving from the first client. Do you have any solution?
Here is the server code
using namespace std;
void *task1(void *);
static int connFd;
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char* argv[])
{
int pId, portNo, listenFd;
socklen_t len; //store size of the address
bool loop = false;
struct sockaddr_in svrAdd, clntAdd;
pthread_t threadA[3];
if (argc < 2)
{
cerr << "Syntam : ./server <port>" << endl;
return 0;
}
portNo = atoi(argv[1]);
if((portNo > 65535) || (portNo < 2000))
{
cerr << "Please enter a port number between 2000 - 65535" << endl;
return 0;
}
//create socket
listenFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(listenFd < 0)
{
cerr << "Cannot open socket" << endl;
return 0;
}
bzero((char*) &svrAdd, sizeof(svrAdd));
svrAdd.sin_family = AF_INET;
svrAdd.sin_addr.s_addr = INADDR_ANY;
svrAdd.sin_port = htons(portNo);
//bind socket
if(bind(listenFd, (struct sockaddr *)&svrAdd, sizeof(svrAdd)) < 0)
{
cerr << "Cannot bind" << endl;
return 0;
}
listen(listenFd, 5);
int noThread = 0;
while (noThread < 3)
{
socklen_t len = sizeof(clntAdd);
cout << "Listening" << endl;
//this is where client connects. svr will hang in this mode until client conn
connFd = accept(listenFd, (struct sockaddr *)&clntAdd, &len);
if (connFd < 0)
{
cerr << "Cannot accept connection" << endl;
return 0;
}
else
{
cout << "Connection successful" << endl;
}
pthread_create(&threadA[noThread], NULL, task1, NULL);
noThread++;
}
for(int i = 0; i < 3; i++)
{
pthread_join(threadA[i], NULL);
}
}
void *task1 (void *dummyPt)
{
cout << "Thread No: " << pthread_self() << endl;
char test[256];
bzero(test, 256);
bool loop = false;
while(!loop)
{
bzero(test, 256);
int n = read(connFd, test, 255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",test);
}
cout << "\nClosing thread and conn" << endl;
close(connFd);
}
And the client code
using namespace std;
int main (int argc, char* argv[])
{
int listenFd, portNo;
bool loop = false;
struct sockaddr_in svrAdd;
struct hostent *server;
if(argc < 3)
{
cerr<<"Syntax : ./client <host name> <port>"<<endl;
return 0;
}
portNo = atoi(argv[2]);
if((portNo > 65535) || (portNo < 2000))
{
cerr<<"Please enter port number between 2000 - 65535"<<endl;
return 0;
}
//create client skt
listenFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(listenFd < 0)
{
cerr << "Cannot open socket" << endl;
return 0;
}
server = gethostbyname(argv[1]);
if(server == NULL)
{
cerr << "Host does not exist" << endl;
return 0;
}
bzero((char *) &svrAdd, sizeof(svrAdd));
svrAdd.sin_family = AF_INET;
bcopy((char *) server -> h_addr, (char *) &svrAdd.sin_addr.s_addr, server -> h_length);
svrAdd.sin_port = htons(portNo);
int checker = connect(listenFd,(struct sockaddr *) &svrAdd, sizeof(svrAdd));
if (checker < 0)
{
cerr << "Cannot connect!" << endl;
return 0;
}
//send stuff to server
for(;;)
{
char s[300];
//cin.clear();
//cin.ignore(256, '\n');
cout << "Enter stuff: ";
bzero(s, 300);
cin.getline(s, 300);
write(listenFd, s, strlen(s));
}
}
Yor connFd is a global variable, which you access from your main thread and all handling threads. This will not do! Imagine that - you've accepted the first connection and set the variable to the receiving socket. You've spawn the handling thread, which started reading. Next thing you know, another connection is coming along and you are receiving it as well! This very moment connFd points to the new connection, so the thread which is already using it will suddenly switch to the new connection! Of course it is not good.
The way to fix this problem is to pass the connection to the thread in such a way that is is not shared across threads. And easiest way of doing so is to use C++ thread class.
For example, this is code fragment illustrating the above idea:
void handle_connection(int fd) {
... <your task1 code>
}
...
std::vector<std::thread> threads;
...
int conn = accept(listenFd, (struct sockaddr *)&clntAdd, &len);
threads.push_back(std::thread(&handle_connection, conn));
...
... (in the end)
for (auto&& t : threads)
t.join();
The problem: send and receive an array of integers (later floats) from client to server using TCP and the sockets C API. Must run both in Winsock and UNIX.
In the future different endianess for client/server can be handled, but now the test was made between 2 machines with same endianess (Windows client, Linux server).
I implemented the client and server, all seems to work, but the question is a doubt on how the send() (client) and recv() (server) calls handle the way my implementation is made. Or, in other words, if the approach has a flaw.
The approach was:
On the client generate a vector of uint8_t according to a predefined algorithm (N sequences of values from 0 to 254). This sequence is reproduced in the server to compare
with the incoming data (by comparing 2 vectors).
On the client, send the array size
On the client, send the array using a loop on the array, call send() for each element.
On the server, recv() the array size.
On the server, recv() the array using a loop on the array size, call recv() for each element.
To check my approach,
I save the bytes received on the server to a file inside the previous recv() loop
After the loop , read this file, generate another vector with the same size according to step 1), compare the 2 vectors.
They match, using tests up 255,000,000 array elements sent and received.
Question:
One can then assume that the server recv() loop is guaranteed to match the client send() loop?
Or, in other words, that the array indices arrive in the same order?
I am following the excellent "TCP/IP Sockets in C" (Donahoo, Calvert) and on the example of echo client / server
http://cs.baylor.edu/~donahoo/practical/CSockets/
Quote:
"The bytes sent by a call to send() on one end of a connection may not all be returned by a single call to recv() on the other end."
The recv() part is handled differently in this example, a loop is made until the total number of bytes received matches the (known size) of bytes sent, according to:
while (totalBytesRcvd < echoStringLen)
{
bytesRcvd = recv(sock, echoBuffer, RCVBUFSIZE - 1, 0))
totalBytesRcvd += bytesRcvd; /* Keep tally of total bytes */
}
Complete example:
http://cs.baylor.edu/~donahoo/practical/CSockets/code/TCPEchoClient.c
But this case is for one send() with multiple bytes call that might be not received all at once.
In my case there are N send calls (1 byte each) and N receive calls (1 byte each), that happen to be made in the same order.
Question:
Does the TCP/IP protocol guarantee that the multiple send calls (that have sequential time stamps)
are guaranteed to be received in order? Or time not an issue here?
Some research:
When sending an array of int over TCP, why are only the first amount correct?
Quote:
"There is nothing to guarantee how TCP will packet up the data you send to a stream - it only guarantees that it will end up in the correct order at the application level."
Some more links
How do I send an array of integers over TCP in C?
Thanks
EDIT : code edited with main() functions and usage, and variable names for clarity
Usage example: send N sequences 1 time to server at IP-address
./client -i IP-address -n N -d
Code: Client.cpp
#if defined (_MSC_VER)
#include <winsock.h>
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include <iostream>
#include <stdint.h>
#include <vector>
const unsigned short server_port = 5000; // server port
void client_echo_text(const char *server_ip);
void client_send_data(const char *server_ip, const uint32_t arr_size_mult);
///////////////////////////////////////////////////////////////////////////////////////
//main
///////////////////////////////////////////////////////////////////////////////////////
// usage:
// send N sequences 1 time to server at <IP adress>
// ./client -i <IP adress> -n N -d
// same with infinite loop
// ./client -i <IP adress> -n N -l
int main(int argc, char *argv[])
{
char server_ip[255]; // server IP address (dotted quad)
strcpy(server_ip, "127.0.0.1");
uint32_t arr_size_mult = 10;
//no arguments
if (argc == 1)
{
client_send_data(server_ip, arr_size_mult);
}
for (int i = 1; i < argc && argv[i][0] == '-'; i++)
{
switch (argv[i][1])
{
case 'i':
strcpy(server_ip, argv[i + 1]);
i++;
break;
case 'e':
client_echo_text(server_ip);
exit(0);
break;
case 'n':
arr_size_mult = atoi(argv[i + 1]);
i++;
break;
case 'd':
client_send_data(server_ip, arr_size_mult);
exit(0);
break;
case 'l':
while (true)
{
client_send_data(server_ip, arr_size_mult);
}
break;
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////
//client_send_data
///////////////////////////////////////////////////////////////////////////////////////
void client_send_data(const char *server_ip, const uint32_t arr_size_mult)
{
int sock; // socket descriptor
struct sockaddr_in server_addr; // server address
//data
const uint32_t arr_size = arr_size_mult * 255; // array size
//construct array
std::vector<uint8_t> val8(arr_size);
uint8_t v8 = 0;
for (size_t i = 0; i < arr_size; ++i)
{
val8[i] = v8;
v8++;
if (v8 == 255)
{
v8 = 0;
}
}
#if defined (_MSC_VER)
WSADATA ws_data;
if (WSAStartup(MAKEWORD(2, 0), &ws_data) != 0)
{
exit(1);
}
#endif
// create a stream socket using TCP
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
exit(1);
}
// construct the server address structure
memset(&server_addr, 0, sizeof(server_addr)); // zero out structure
server_addr.sin_family = AF_INET; // internet address family
server_addr.sin_addr.s_addr = inet_addr(server_ip); // server IP address
server_addr.sin_port = htons(server_port); // server port
// establish the connection to the server
if (connect(sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0)
{
std::cout << "connect error: " << strerror(errno) << std::endl;
exit(1);
}
//send array size
if (send(sock, (char *)&arr_size, sizeof(uint32_t), 0) != sizeof(uint32_t))
{
exit(1);
}
std::cout << "client sent array size: " << (int)arr_size << std::endl;
//send array
for (size_t i = 0; i < arr_size; ++i)
{
v8 = val8[i];
if (send(sock, (char *)&v8, sizeof(uint8_t), 0) != sizeof(uint8_t))
{
exit(1);
}
}
std::cout << "client sent array: " << std::endl;
#if defined (_MSC_VER)
closesocket(sock);
WSACleanup();
#else
close(sock);
#endif
}
Code: Server.cpp
if defined(_MSC_VER)
#include <winsock.h>
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include <iostream>
#include <stdint.h>
#include <assert.h>
#include <vector>
const unsigned short server_port = 5000; // server port
void server_echo_text();
void server_recv_data(bool verbose);
void check_file(const uint32_t arr_size, bool verbose, const size_t slab_size);
///////////////////////////////////////////////////////////////////////////////////////
//main
///////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
bool verbose = false;
//no arguments
if (argc == 1)
{
server_recv_data(verbose);
}
for (int i = 1; i < argc && argv[i][0] == '-'; i++)
{
switch (argv[i][1])
{
case 'v':
std::cout << "verbose mode: " << std::endl;
verbose = true;
break;
case 'e':
std::cout << "running echo server: " << std::endl;
server_echo_text();
exit(0);
break;
case 'd':
std::cout << "running data server: " << std::endl;
server_recv_data(verbose);
exit(0);
break;
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////
//server_recv_data
///////////////////////////////////////////////////////////////////////////////////////
void server_recv_data(bool verbose)
{
const int MAXPENDING = 5; // maximum outstanding connection requests
int server_socket; // socket descriptor for server
int client_socket; // socket descriptor for client
sockaddr_in server_addr; // local address
sockaddr_in client_addr; // client address
int recv_size; // size in bytes returned by recv()
#if defined (_MSC_VER)
int len_addr; // length of client address data structure
#else
socklen_t len_addr;
#endif
//data
uint32_t arr_size = 0;
size_t slab_size = 1;
FILE *file;
#if defined (_MSC_VER)
WSADATA ws_data;
if (WSAStartup(MAKEWORD(2, 0), &ws_data) != 0)
{
exit(1);
}
#endif
// create socket for incoming connections
if ((server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
exit(1);
}
// construct local address structure
memset(&server_addr, 0, sizeof(server_addr)); // zero out structure
server_addr.sin_family = AF_INET; // internet address family
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // any incoming interface
server_addr.sin_port = htons(server_port); // local port
// bind to the local address
if (bind(server_socket, (sockaddr*)&server_addr, sizeof(server_addr)) < 0)
{
//bind error: Permission denied
//You're probably trying to bind a port under 1024. These ports usually require root privileges to be bound.
std::cout << "bind error: " << strerror(errno) << std::endl;
exit(1);
}
// mark the socket so it will listen for incoming connections
if (listen(server_socket, MAXPENDING) < 0)
{
exit(1);
}
for (;;) // run forever
{
// set length of client address structure (in-out parameter)
len_addr = sizeof(client_addr);
// wait for a client to connect
if ((client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &len_addr)) < 0)
{
exit(1);
}
// convert IP addresses from a dots-and-number string to a struct in_addr and back
char *str_ip = inet_ntoa(client_addr.sin_addr);
std::cout << "handling client " << str_ip << std::endl;
// receive array size
if ((recv_size = recv(client_socket, (char *)&arr_size, sizeof(uint32_t), 0)) != sizeof(uint32_t))
{
exit(1);
}
std::cout << "server received array size: " << (int)arr_size << std::endl;
//save file
file = fopen("file.bin", "wb");
fwrite(&arr_size, sizeof(uint32_t), 1, file);
//receive array
for (size_t i = 0; i < arr_size; ++i)
{
uint8_t v8;
if ((recv_size = recv(client_socket, (char *)&v8, sizeof(uint8_t), 0)) != sizeof(uint8_t))
{
exit(1);
}
//write 1 element
fwrite(&v8, sizeof(uint8_t), slab_size, file);
}
fclose(file);
std::cout << "server received array: " << std::endl;
check_file(arr_size, verbose, slab_size);
// close client socket
#if defined (_MSC_VER)
closesocket(client_socket);
#else
close(client_socket);
#endif
}
}
///////////////////////////////////////////////////////////////////////////////////////
//check_file
///////////////////////////////////////////////////////////////////////////////////////
void check_file(const uint32_t arr_size, bool verbose, const size_t slab_size)
{
//read file
std::vector<uint8_t> val8(arr_size);
std::vector<uint8_t> val8_c(arr_size);
uint32_t arr_size_r;
uint8_t v8;
FILE *file;
file = fopen("file.bin", "rb");
fread(&arr_size_r, sizeof(uint32_t), 1, file);
assert(arr_size_r == arr_size);
for (size_t i = 0; i < arr_size; ++i)
{
fread(&v8, sizeof(uint8_t), slab_size, file);
val8[i] = v8;
if (verbose) std::cout << (int)val8[i] << " ";
}
if (verbose) std::cout << std::endl;
fclose(file);
//check data, define array the same as in client, compare arrays
v8 = 0;
for (size_t i = 0; i < arr_size; ++i)
{
val8_c[i] = v8;
v8++;
if (v8 == 255)
{
v8 = 0;
}
}
//compare arrays
for (size_t i = 0; i < arr_size; ++i)
{
if (val8_c[i] != val8[i])
{
std::cout << "arrays differ at: " << i << " " << (int)val8_c[i] << " " << (int)val8[i] << std::endl;
assert(0);
}
}
std::cout << "arrays match: " << (int)arr_size << " " << (int)arr_size_r << std::endl;
std::cout << std::endl;
}
TCP is a streaming protocol, it guarantees the exact replication of the sent stream at receiver. So yes, ordering will match, always. The protocol stack will reorder messages if they come out of order. So if you reliably catch the beginning of the stream and the end of the stream then everything in between will come in order and in the good shape.
I am not sure though you'd ever want to send a single number and not pre-marshal them into a large buffer. You will get several orders of magnitude improvement in performance.
As #usr pointed out, the loops are badly constructed. What is needed are "send all" and "receive all" functions.
These ones are based on the book by Stevens "UNIX Network Programming: Sockets Introduction"
http://www.informit.com/articles/article.aspx?p=169505&seqNum=9
Send all function and send function from client:
void send_all(int sock, const void *vbuf, size_t size_buf)
{
const char *buf = (char*)vbuf; // can't do pointer arithmetic on void*
int send_size; // size in bytes sent or -1 on error
size_t size_left; // size left to send
const int flags = 0;
size_left = size_buf;
while (size_left > 0)
{
if ((send_size = send(sock, buf, size_left, flags)) == -1)
{
std::cout << "send error: " << strerror(errno) << std::endl;
exit(1);
}
if (send_size == 0)
{
std::cout << "all bytes sent " << std::endl;
break;
}
size_left -= send_size;
buf += send_size;
}
return;
}
///////////////////////////////////////////////////////////////////////////////////////
//client_send_data
///////////////////////////////////////////////////////////////////////////////////////
void client_send_data(const char *server_ip, const uint32_t arr_size_mult)
{
int sock; // socket descriptor
struct sockaddr_in server_addr; // server address
//data
const uint32_t arr_size = arr_size_mult * 255; // array size
//construct array
std::vector<uint8_t> val8(arr_size);
uint8_t v8 = 0;
for (size_t i = 0; i < arr_size; ++i)
{
val8[i] = v8;
v8++;
if (v8 == 255)
{
v8 = 0;
}
}
#if defined (_MSC_VER)
WSADATA ws_data;
if (WSAStartup(MAKEWORD(2, 0), &ws_data) != 0)
{
exit(1);
}
#endif
// create a stream socket using TCP
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
exit(1);
}
// construct the server address structure
memset(&server_addr, 0, sizeof(server_addr)); // zero out structure
server_addr.sin_family = AF_INET; // internet address family
server_addr.sin_addr.s_addr = inet_addr(server_ip); // server IP address
server_addr.sin_port = htons(server_port); // server port
// establish the connection to the server
if (connect(sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0)
{
std::cout << "connect error: " << strerror(errno) << std::endl;
exit(1);
}
//send array size
send_all(sock, (void *)&arr_size, sizeof(uint32_t));
std::cout << "client sent array size: " << (int)arr_size << std::endl;
//send array
//std::vector.data() returns the address of the initial element in the container (C++11)
send_all(sock, (void *)val8.data(), sizeof(uint8_t) * val8.size());
std::cout << "client sent array: " << std::endl;
#if defined (_MSC_VER)
closesocket(sock);
WSACleanup();
#else
close(sock);
#endif
}
Receive all function
void recv_all(int sock, void *vbuf, size_t size_buf, FILE *file)
{
char *buf = (char*)vbuf; // can't do pointer arithmetic on void*
int recv_size; // size in bytes received or -1 on error
size_t size_left; // size left to send
const int flags = 0;
size_left = size_buf;
while (size_left > 0)
{
if ((recv_size = recv(sock, buf, size_left, flags)) == -1)
{
std::cout << "recv error: " << strerror(errno) << std::endl;
exit(1);
}
if (recv_size == 0)
{
std::cout << "all bytes received " << std::endl;
break;
}
//save to local file
fwrite(buf, recv_size, 1, file);
size_left -= recv_size;
buf += recv_size;
}
return;
}
///////////////////////////////////////////////////////////////////////////////////////
//server_recv_data
///////////////////////////////////////////////////////////////////////////////////////
void server_recv_data(bool verbose)
{
const int MAXPENDING = 5; // maximum outstanding connection requests
int server_socket; // socket descriptor for server
int client_socket; // socket descriptor for client
sockaddr_in server_addr; // local address
sockaddr_in client_addr; // client address
#if defined (_MSC_VER)
int len_addr; // length of client address data structure
#else
socklen_t len_addr;
#endif
//data
uint32_t arr_size = 0;
const size_t slab_size = 1;
FILE *file;
#if defined (_MSC_VER)
WSADATA ws_data;
if (WSAStartup(MAKEWORD(2, 0), &ws_data) != 0)
{
exit(1);
}
#endif
// create socket for incoming connections
if ((server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
exit(1);
}
// construct local address structure
memset(&server_addr, 0, sizeof(server_addr)); // zero out structure
server_addr.sin_family = AF_INET; // internet address family
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // any incoming interface
server_addr.sin_port = htons(server_port); // local port
// bind to the local address
if (bind(server_socket, (sockaddr*)&server_addr, sizeof(server_addr)) < 0)
{
//bind error: Permission denied
//You're probably trying to bind a port under 1024. These ports usually require root privileges to be bound.
std::cout << "bind error: " << strerror(errno) << std::endl;
exit(1);
}
// mark the socket so it will listen for incoming connections
if (listen(server_socket, MAXPENDING) < 0)
{
exit(1);
}
for (;;) // run forever
{
// set length of client address structure (in-out parameter)
len_addr = sizeof(client_addr);
// wait for a client to connect
if ((client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &len_addr)) < 0)
{
exit(1);
}
// convert IP addresses from a dots-and-number string to a struct in_addr and back
char *str_ip = inet_ntoa(client_addr.sin_addr);
std::cout << "handling client " << str_ip << std::endl;
///////////////////////////////////////////////////////////////////////////////////////
//receive data and save to local file as received
///////////////////////////////////////////////////////////////////////////////////////
//save local file
file = fopen(check_file_name.c_str(), "wb");
//receive/save array size
recv_all(client_socket, &arr_size, sizeof(uint32_t), file);
std::cout << "server received array size: " << (int)arr_size << std::endl;
//receive/save array
uint8_t *buf = new uint8_t[arr_size];
recv_all(client_socket, buf, sizeof(uint8_t) * arr_size, file);
delete[] buf;
fclose(file);
std::cout << "server received array: " << std::endl;
//check
check_file(arr_size, verbose, slab_size);
// close client socket
#if defined (_MSC_VER)
closesocket(client_socket);
#else
close(client_socket);
#endif
}
}