I am writing a network program in C ++. How it works:
The server streams video for the client in real time.
The client receives the video data.
I tried to set up sending and receiving messages (right during streaming). For this, I started the video in a separate thread.
Messages from the client to the server reach the server without any problems. But if you send messages from the server to the client, then firstly the video itself changes, and some kind of garbage is displayed.
How it works (no)
I understand that this is due to the fact that the client is constantly receiving messages (vide). But is it possible to somehow solve this problem?
Server code
#include "server.h"
int Server::open()
{
WORD DLLVersion = MAKEWORD(2, 1);
if (WSAStartup(DLLVersion, &wsaData) != 0)
{
std::cout << "Error" << std::endl;
exit(1);
}
int sizeofaddr = sizeof(addr);
addr.sin_addr.s_addr = inet_addr(addrss);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
server = socket(AF_INET, SOCK_STREAM, NULL);
bind(server, (SOCKADDR*)&addr, sizeof(addr));
listen(server, SOMAXCONN);
std::cout << "Listening..." << std::endl;
client = accept(server, (SOCKADDR*)&addr, &sizeofaddr);
if (client == 0) {
std::cout << "Error #2\n";
}
else {
std::cout << "Client Connected!\n";
}
}
int Server::sendData(const char *buffer, int len)
{
return send(client, buffer, len, NULL);
}
int Server::recvData(char *buffer, int len)
{
return recv(client, buffer, len, NULL);
}
int Server::close()
{
closesocket(server);
std::cout << "Server is closed" << std::endl;
return 0;
}
Server main
#include "server.h"
#include "opencv2/core/core.hpp"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2\video\background_segm.hpp"
#include <opencv2\video\tracking.hpp>
#include <Windows.h>
#include <thread>
#include <string>
#define FRAME_WIDTH 640
#define FRAME_HEIGHT 480
#define CLIENT_CLOSE_CONNECTION_SYMBOl '#'
cv::Mat cameraFeed;
int height, width, IM_HEIGHT, IM_WIDTH, imgSize;
bool is_client_connection_close(const char *msg)
{
for (int i = 0; i < strlen(msg); ++i)
{
if (msg[i] == CLIENT_CLOSE_CONNECTION_SYMBOl)
{
return true;
}
}
return false;
}
void VideoSender(Server server)
{
cv::VideoCapture cap(0);
while (true)
{
cv::Mat frame, image;
if (!cap.read(cameraFeed))
{
std::cout << "Video dont work" << std::endl;
break;
}
height = cameraFeed.rows;
width = cameraFeed.cols;
IM_HEIGHT = FRAME_HEIGHT;
IM_WIDTH = FRAME_WIDTH;
resize(cameraFeed, cameraFeed, cv::Size(IM_WIDTH, IM_HEIGHT));
imgSize = cameraFeed.total()*cameraFeed.elemSize();
server.sendData(reinterpret_cast<char*>(cameraFeed.data), imgSize);
imshow("Video_sending", cameraFeed);
char button = cv::waitKey(10);
if (button == 'q')
{
std::cout << "Stop sending video data..." << std::endl;
cap.release();
cv::destroyWindow("Video_sending");
server.close();
break;
}
}
}
int main(int argc, char* argv[])
{
Server server;
server.open();
std::thread th_video(VideoSender, server);
while (true)
{
//sending message
std::string msg1;
std::getline(std::cin, msg1);
int msg_size1 = msg1.size();
server.sendData((char*)&msg_size1, sizeof(int));
server.sendData(msg1.c_str(), msg_size1);
//receive_message
int msg_size;
server.recvData((char*)&msg_size, sizeof(int));
char *msg = new char[msg_size + 1];
msg[msg_size] = '\0';
server.recvData(msg, msg_size);
std::cout << "Message from client: " << msg
<< " (size: " << msg_size << ")"
<< std::endl;
delete[] msg;
}
system("pause");
return 0;
}
Client code
#include "client.h"
int Client::open()
{
WORD DLLVersion = MAKEWORD(2, 1);
if (WSAStartup(DLLVersion, &wsaData) != 0) {
std::cout << "Error" << std::endl;
exit(1);
}
int sizeofaddr = sizeof(addr);
addr.sin_addr.s_addr = inet_addr(addrs);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
server = socket(AF_INET, SOCK_STREAM, NULL);
if (connect(server, (SOCKADDR*)&addr, sizeof(addr)) != 0) {
std::cout << "Error: failed connect to server.\n";
return 1;
}
std::cout << "Connected!\n";
}
int Client::sendData(const char * buffer, int len)
{
return send(server, buffer, len, NULL);
}
int Client::recvData(char *buffer, int len)
{
return recv(server, buffer, len, NULL);
}
int Client::close()
{
closesocket(server);
std::cout << "Server is closed" << std::endl;
return 0;
}
Client main
#include "client.h"
#include "opencv2/core/core.hpp"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2\video\background_segm.hpp"
#include <opencv2\video\tracking.hpp>
#include <Windows.h>
#include <string>
#include <thread>
#pragma warning(disable: 4996)
#define FRAME_WIDTH 640
#define FRAME_HEIGHT 480
int imgSize;
int bytes = 0;
bool running = true;
int msg_size;
void VideoReceiver(Client client)
{
while (running)
{
int IM_HEIGHT = FRAME_HEIGHT;
int IM_WIDTH = FRAME_WIDTH;
cv::Mat img = cv::Mat::zeros(FRAME_HEIGHT, FRAME_WIDTH, CV_8UC3);
imgSize = img.total()*img.elemSize();
const int ah = 921600;
char sockData[ah];
for (int i = 0; i < imgSize; i += bytes)
if ((bytes = client.recvData(sockData + i, imgSize - i)) == 0)
std::cout << "WTF" << std::endl;
int ptr = 0;
for (int i = 0; i < img.rows; ++i)
for (int j = 0; j < img.cols; ++j)
{
img.at<cv::Vec3b>(i, j) = cv::Vec3b(sockData[ptr + 0], sockData[ptr + 1], sockData[ptr + 2]);
ptr = ptr + 3;
}
cv::namedWindow("Reveived_video", cv::WINDOW_AUTOSIZE);
imshow("Reveived_video", img);
char button = cv::waitKey(10);
if (button == 'q')
{
std::cout << "Stop reveiving video data..." << std::endl;
cv::destroyWindow("Reveived_video");
client.close();
break;
}
}
}
void message_sendind(Client client)
{
while (true)
{
//receiv message
int msg_size;
client.recvData((char*)&msg_size, sizeof(int));
char *msg = new char[msg_size + 1];
msg[msg_size] = '\0';
client.recvData(msg, msg_size);
std::cout << "Message from server: " << msg
<< " (size: " << msg_size << ")"
<< std::endl;
delete[] msg;
//sending message
//std::string msg1;
//std::getline(std::cin, msg1);
//int msg_size1 = msg1.size();
//client.sendData((char*)&msg_size1, sizeof(int));
//client.sendData(msg1.c_str(), msg_size1);
}
}
int main(int argc, char* argv[])
{
Client client;
client.open();
std::thread th_video(VideoReceiver, client);
while (true)
{
//receiv message
int msg_size;
client.recvData((char*)&msg_size, sizeof(int));
char *msg = new char[msg_size + 1];
msg[msg_size] = '\0';
client.recvData(msg, msg_size);
std::cout << "Message from server: " << msg
<< " (size: " << msg_size << ")"
<< std::endl;
delete[] msg;
//sending message
std::string msg1;
std::getline(std::cin, msg1);
int msg_size1 = msg1.size();
client.sendData((char*)&msg_size1, sizeof(int));
client.sendData(msg1.c_str(), msg_size1);
}
system("pause");
return 0;
}
Related
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.
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.
I have the following code that is the "server.cpp". It correctly establishes a connection with the client and streams the video. The problem is that when I close the client, regardless of the while cycle in the main program, automatically also the server will shout down, even if I am using pthread_create.
Could you help me to find a solution to avoid it to close, and to remain alive until a new client will connect to it?
/**
* OpenCV video streaming over TCP/IP
* Server: Captures video from a webcam and send it to a client
* by Isaac Maia
*/
#include "opencv2/opencv.hpp"
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
#include <string.h>
using namespace cv;
void *display(void *);
int capDev = 0;
VideoCapture cap(capDev); // open the default camera
int main(int argc, char** argv)
{
//--------------------------------------------------------
//networking stuff: socket, bind, listen
//--------------------------------------------------------
int localSocket, remoteSocket, port = 4097;
struct sockaddr_in localAddr, remoteAddr;
pthread_t thread_id;
int addrLen = sizeof(struct sockaddr_in);
if ((argc > 1) && (strcmp(argv[1], "-h") == 0))
{
std::cerr << "usage: ./cv_video_srv [port] [capture device]\n"
<< "port : socket port (4097 default)\n"
<< "capture device : (0 default)\n" << std::endl;
exit(1);
}
if (argc == 2)
port = atoi(argv[1]);
localSocket = socket(AF_INET, SOCK_STREAM, 0);
if (localSocket == -1)
{
perror("socket() call failed!!");
}
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = INADDR_ANY;
localAddr.sin_port = htons(port);
if (bind(localSocket, (struct sockaddr *) &localAddr, sizeof(localAddr))
< 0)
{
perror("Can't bind() socket");
exit(1);
}
//Listening
listen(localSocket, 3);
std::cout << "Waiting for connections...\n" << "Server Port:" << port
<< std::endl;
//accept connection from an incoming client
while (1)
{
//if (remoteSocket < 0) {
// perror("accept failed!");
// exit(1);
//}
remoteSocket = accept(localSocket,
(struct sockaddr *) &remoteAddr,
(socklen_t*) &addrLen);
//std::cout << remoteSocket<< "32"<< std::endl;
if (remoteSocket < 0)
{
perror("accept failed!");
exit(1);
}
std::cout << "Connection accepted" << std::endl;
pthread_create(&thread_id, NULL, display, &remoteSocket);
//pthread_join(thread_id,NULL);
}
//pthread_join(thread_id,NULL);
//close(remoteSocket);
return 0;
}
void *display(void *ptr)
{
int socket = *(int *) ptr;
//OpenCV Code
//----------------------------------------------------------
Mat img, imgGray;
img = Mat::zeros(480, 640, CV_8UC1);
//make it continuous
if (!img.isContinuous())
{
img = img.clone();
}
int imgSize = img.total() * img.elemSize();
int bytes = 0;
int key;
//make img continuos
if (!img.isContinuous())
{
img = img.clone();
imgGray = img.clone();
}
std::cout << "Image Size:" << imgSize << std::endl;
while (1)
{
/* get a frame from camera */
cap >> img;
//do video processing here
cvtColor(img, imgGray, CV_BGR2GRAY);
//send processed image
if ((bytes = send(socket, imgGray.data, imgSize, 0)) < 0)
{
std::cerr << "bytes = " << bytes << std::endl;
break;
}
}
}
You are not passing remoteSocket to display() safely, so you have a race condition that could alter the value of remoteSocket if another client connects before display() has a chance to use the previous value. Don't pass a pointer to the original remoteSocket, pass a copy of it instead.
Also, display() is not closing the accepted socket when done using it before exiting, so you are leaking socket descriptors. Eventually, accept() will fail due to lack of available descriptors, and your main loop will exit the program.
Try something more like this instead:
/**
* OpenCV video streaming over TCP/IP
* Server: Captures video from a webcam and send it to a client
* by Isaac Maia
*/
#include "opencv2/opencv.hpp"
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
#include <string.h>
#include <cstdlib>
using namespace cv;
void* display(void *);
int capDev = 0;
VideoCapture cap(capDev); // open the default camera
int main(int argc, char** argv)
{
//--------------------------------------------------------
//networking stuff: socket, bind, listen
//--------------------------------------------------------
int localSocket, remoteSocket;
unsigned short port = 4097;
struct sockaddr_in localAddr, remoteAddr;
socklen_t addrLen;
pthread_t thread_id;
int err;
if (argc > 1)
{
long lport = strtol(argv[1], NULL, 10);
if ((lport <= 0) || (lport > 65535))
{
std::cerr << "usage: ./cv_video_srv [port] [capture device]\n"
<< "port : socket port (4097 default)\n"
<< "capture device : (0 default)\n" << std::endl;
return 1;
}
port = (unsigned short) lport;
}
localSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (localSocket == -1)
{
perror("socket() call failed");
return 1;
}
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = INADDR_ANY;
localAddr.sin_port = htons(port);
if (bind(localSocket, (struct sockaddr *) &localAddr, sizeof(localAddr)) < 0)
{
perror("Can't bind() socket");
close(localSocket);
return 1;
}
//Listening
if (listen(localSocket, 3) < 0)
{
perror("Can't listen() socket");
close(localSocket);
return 1;
}
std::cout << "Waiting for connections...\n"
<< "Server Port:" << port << std::endl;
//accept connection from an incoming client
while (1)
{
addrLen = sizeof(struct sockaddr_in);
remoteSocket = accept(localSocket,
(struct sockaddr *) &remoteAddr,
&addrLen);
//std::cout << remoteSocket << std::endl;
if (remoteSocket < 0)
{
perror("accept failed");
close(localSocket);
return 1;
}
std::cout << "Connection accepted" << std::endl;
int *socket = new int(remoteSocket);
err = pthread_create(&thread_id, NULL, display, socket);
if (err != 0)
{
//perror("pthread_create failed");
std::cerr << "pthread_create failed: " << strerror(err) << std::endl;
close(remoteSocket);
delete socket;
continue;
}
//pthread_join(thread_id,NULL);
}
//pthread_join(thread_id,NULL);
return 0;
}
void* display(void *ptr)
{
int socket = * (int*) ptr;
delete (int*) ptr;
//OpenCV Code
//----------------------------------------------------------
Mat img, imgGray;
img = Mat::zeros(480, 640, CV_8UC1);
//make it continuous
if (!img.isContinuous())
{
img = img.clone();
}
int imgSize = img.total() * img.elemSize();
int bytes = 0;
int key;
//make img continuos
if (!img.isContinuous())
{
img = img.clone();
imgGray = img.clone();
}
std::cout << "Image Size:" << imgSize << std::endl;
while (1)
{
/* get a frame from camera */
cap >> img;
//do video processing here
cvtColor(img, imgGray, CV_BGR2GRAY);
//send processed image
bytes = send(socket, imgGray.data, imgSize, 0);
if (bytes <= 0)
{
if (bytes < 0)
perror("send failed");
break;
}
}
close(socket);
return NULL;
}
sorry for taking too late to the answer.
Your problem is in this line:
send(socket, imgGray.data, imgSize, 0);
According to docs, send signs a SIGPIPE signal:
SIGPIPE signal if the peer on a stream-oriented socket has closed the
connection
and according to this
The default action ... is to cause the process to terminate.
The solution is using MSG_NOSIGNAL such as
send(socket, imgGray.data, imgSize, MSG_NOSIGNAL);
MSG_NOSIGNAL is a per-call feature, ignoring SIGPIPE sets a process attribute that affects all threads in the process.
You can detect that this error occurred by checking if the EPIPE error returned. More details about this can be found in the mentioned docs.
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <iostream>
#include <pthread.h>
int f(int x) {
if (x == 0) return 1;
return x % 100;
}
void *deal(void *arg) {
int fd = * (int *) arg;
int x;
int n = read(fd, &x, sizeof x);
if (n < 0) std::cout << "error reading" << std::endl;
std::cout << "request " << x << " received" << std::endl;
int y = f(x);
write(fd, &y, sizeof y);
std::cout << "response to " << x << ": " << y << std::endl;
close(fd);
return NULL;
}
int main(int argc, char *argv[]) {
int port = atoi(argv[1]);
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
bind(socket_fd, (sockaddr *) &addr, sizeof addr);
listen(socket_fd, 20);
std::cout << "listen on " << port << std::endl;
sockaddr remote_addr;
socklen_t len;
for (; ;) {
int fd = accept(socket_fd, &remote_addr, &len);
pthread_t p;
pthread_create(&p, NULL, deal, (void *) &fd);
}
return 0;
}
This is the code of the server. It simply create a new thread for each connection and then read an integer, then reply the integer % 100.
It works well when there are no concurrent requests. However, it should also work on concurrent. But it failed on just 10 concurrent requests.
You are using the memory for fd time and time again in different threads. I.e. it is getting overwritten on each accept.
Change
pthread_create(&p, NULL, deal, (void *) &fd);
to
pthread_create(&p, NULL, deal, (void *) fd);
And
void *deal(void *arg) {
int fd = * (int *) arg;
to
void *deal(void *arg) {
int fd = (int) arg;
I'm trying to download picture from website. The problem is that even tho I get all the content from HTTP response body, file won't open. I've been trying to solve this but I can't find the real problem. One thing I did notice is that picture downloaded using chromium displays different characters than picture downloaded from my code using command:
$ cat picture.png | less
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
using std::cout;
using std::endl;
//with this function I remove HTTP header info, so I only get content.
char *removeHTTPHeader(char *buffer) {
char *t = strstr(buffer, "\r\n\r\n");
t = t + 4;
return t;
}
void getPicture(const int &socketfd, const int &bSize) {
std::ofstream file("picture.png",
std::ofstream::binary | std::ofstream::out);
char buffer[bSize];
ssize_t bReceived;
bReceived = recv(socketfd, buffer, bSize, 0);
char *t = removeHTTPHeader(buffer);
file.write(t, strlen(t));
memset(buffer, 0, bSize);
while ((bReceived = recv(socketfd, buffer, bSize, 0)) > 0) {
file.write(buffer, bReceived);
memset(buffer, 0, bSize);
}
file.close();
}
int main() {
int status;
addrinfo host_info;
addrinfo *host_info_list;
memset(&host_info, 0, sizeof(host_info));
host_info.ai_family = AF_UNSPEC;
host_info.ai_socktype = SOCK_STREAM;
host_info.ai_protocol = 0;
status = getaddrinfo("www.pngimg.com", "80", &host_info, &host_info_list);
if (status != 0) {
cout << "getaddrinfo error" << endl;
}
int socketfd;
socketfd = socket(host_info_list->ai_family, host_info_list->ai_socktype,
host_info_list->ai_protocol);
addrinfo *rp;
for (rp = host_info_list; rp != NULL; rp = rp->ai_next) {
socketfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (socketfd == -1) {
cout << "socket error" << endl;
}
if (connect(socketfd, rp->ai_addr, rp->ai_addrlen) != -1) {
break;
}
close(socketfd);
}
if (rp == NULL) {
cout << "Could not connect!" << endl;
exit(EXIT_FAILURE);
}
freeaddrinfo(host_info_list);
const char *msg =
"GET /upload/water_PNG3290.png HTTP/1.1\r\nhost: www.pngimg.com\r\nConnection: close\r\n\r\n";
status = send(socketfd, msg, strlen(msg), 0);
if (status == -1) {
cout << "error sending" << endl;
exit(EXIT_FAILURE);
}
getPicture(socketfd, 1024);
close(socketfd);
return 0;
}
Here is picture from using cat command:
terminal above is picture from my code, below is picture from chromium "save as"
Problem was that I didn't know that in C style string you can't do strlen on binary data. That's why I had to add counter in function removeHTTPHeader. Below are function getPicture and removeHTTPHeader that I've changed.
char *removeHTTPHeader(char *buffer, int &bodySize) {
char *t = strstr(buffer, "\r\n\r\n");
t = t + 4;
for (auto it = buffer; it != t; ++it) {
++bodySize;
}
return t;
}
void getPicture(const int &socketfd, const int &bSize) {
std::ofstream file("picture.png",
std::ofstream::binary | std::ofstream::out);
char buffer[bSize];
ssize_t bReceived;
bReceived = recv(socketfd, buffer, bSize, 0);
int bodySize = 0;
char *t = removeHTTPHeader(buffer, bodySize);
bodySize = bReceived - bodySize;
file.write(t, bodySize);
memset(buffer, 0, bSize);
while ((bReceived = recv(socketfd, buffer, bSize, 0)) > 0) {
file.write(buffer, bReceived);
memset(buffer, 0, bSize);
}
file.close();
}