i want to write a simple TCP proxy in C++ for the University. The proxy works with two threads one reads from source port and writes to the destination port and the other thread does the same in the other direction. The aim is to read and manipulate the packets in the future. If i use the mutex to lock the port for read and write on the same port i got package loss. Can you help me to locate the problem because i tried it a long time now?
thread1 = 0;
thread2 = 0;
//Client
struct sockaddr_in address;
int size;
if ((create_socket=socket (AF_INET, SOCK_STREAM, 0)) > 0)
printf ("Socket wurde angelegt\n");
address.sin_family = AF_INET;
address.sin_port = htons (PORT);
inet_aton (IP, &address.sin_addr);
if (connect ( create_socket, (struct sockaddr *) &address, sizeof (address)) == 0)
printf ("Verbindung mit dem Server (%s) hergestellt\n", inet_ntoa (address.sin_addr));
//Server
socklen_t addrlen;
struct sockaddr_in address2;
const int y = 1;
if ((create_socket2=socket (AF_INET, SOCK_STREAM, 0)) > 0)
printf ("Socket wurde angelegt\n");
setsockopt( create_socket2, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(int));
address2.sin_family = AF_INET;
address2.sin_addr.s_addr = INADDR_ANY;
address2.sin_port = htons (PORT2);
if (bind ( create_socket2, (struct sockaddr *) &address2, sizeof (address2)) != 0) {
printf( "Der Port ist nicht frei – belegt!\n");
}
listen (create_socket2, 5);
addrlen = sizeof (struct sockaddr_in);
new_socket2 = accept ( create_socket2, (struct sockaddr *) &address2, &addrlen );
if (new_socket2 > 0)
printf ("Ein Client (%s) ist verbunden ...\n", inet_ntoa (address2.sin_addr));
thread apm(apm_gcs);
thread gcs(gcs_apm);
apm.join();
gcs.join();
}
inline void apm_gcs()
{
while (STOP==FALSE)
{
{
lock_guard<mutex> lock(tcp60Mutex);
res = read(create_socket, buffer2, sizeof(buffer2)); // returns after 5 chars have been input
}
{
lock_guard<mutex> lock(tcp65Mutex);
write(new_socket2, buffer2, res);
}
}
}
inline void gcs_apm()
{
while (STOP==FALSE)
{
{
lock_guard<mutex> lock(tcp65Mutex);
res2 = read(new_socket2, buffer, sizeof(buffer)); // returns after 5 chars have been input
}
{
lock_guard<mutex> lock(tcp60Mutex);
write(create_socket, buffer, res2);
}
}
}
Thank you for your help.
Greets
Tobi
There are several things to improve.
First of all: It's not clear what exactly you want to protect. I would understand if you would use one mutex to protect one buffer, and the other mutex for the other buffer, so each buffer will always be accessed by only one thread. However, that does not happen - both threads can read+write the same buffer at the same time. Instead, each mutex protects a socket against read+write at the same time, which is pointless because sockets can handle that perfectly. You can read+write on the same socket at the same time. sockets are used to do that for more than 30 years now.
Once that is changed and your mutexes protect buffers, you will run into blocking again, though less often. You will experience that a thread tries to read or write data while none is available, or the socket connection is full (which happens if you try to quickly write large amounts of data) and it takes time to transfer the data.
This can be solved then by select() or maybe by poll(). Thus the way to go is:
Each thread uses select() or poll() to find out if it can read or write data. Only if it can, it locks the mutex for the buffer, then read or write data (which won't block after select() or poll() made that sure) and then releases the mutex.
Related
I need some help with a socket program with multiple clients and one server. To simplify, I create
3 socket clients
1 socket server
For each client, it opens a new connection for sending a new message and closes the connection after a response is received.
For the server, it does not need to deal with connections concurrently, it can deal with the message one by one
here is my code (runnable), compile it with /usr/bin/g++ mycode.cpp -g -lpthread -lrt -Wall -o mycode
#include <iostream>
#include <arpa/inet.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <unordered_map>
#include <thread>
using namespace std;
void Warning(string msg) { std::cout<< msg << std::endl; }
namespace mySocket {
class Memcached {
public:
// start a server
static void controller(int port=7111) { std::thread (server, port).detach(); }
// open a new connection to send a message:
// 1. open a connection
// 2. send the message
// 3. read the message
// 4. close the connection
std::string sendMessage(string msg, string host, int port=7111) {
int sock = 0, client_fd;
struct sockaddr_in serv_addr;
char buffer[1024] = { 0 };
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
std::cout << "Socket creation error, msg: " << msg << ", host: " << host << ", port: " << port << std::endl;
exit(1);
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
if (inet_pton(AF_INET, host.c_str(), &serv_addr.sin_addr) <= 0) {
std::cout << "\nInvalid address/ Address not supported, kmsgey: " << msg << ", host: " << host << ", port: " << port << std::endl;
exit(1);
}
while ((client_fd = connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))) < 0) { sleep(10*1000); }
std::cout << "client sends a message:"<<msg<<", msg size:"<<msg.size()<<std::endl;
send(sock, msg.c_str(), msg.size(), 0);
read(sock, buffer, 1024);
close(client_fd);
return std::string(buffer, strlen(buffer));
}
private:
// start a server
// 1. open a file descriptor
// 2. listen the fd with queue size 10
// 3. accept one connection at a time
// 4. deal with message in the connection
// 5. accept the next connection
// 6. repeat step 3
static void server(int port) {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = { 0 };
unordered_map<string,string> data;
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
Warning("socket failed"); exit(1);
}
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
Warning("setsockopt failed"); exit(1);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
Warning("bind failed"); exit(1);
}
// the queue size is 10 > 3
if (listen(server_fd, 10) < 0) {
Warning("listen failed"); exit(1);
}
while(1)
{
if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) {
std::cout << "accept failed"; exit(1);
}
memset(&buffer, 0, sizeof(buffer)); //clear the buffer
read(new_socket, buffer, 1024);
std::string msg = std::string(buffer, strlen(buffer));
if (msg.size()==0) {
std::cout<<"I can't believe it"<<std::endl;
}
std::cout<<"received msg from the client:"<<msg<<",msg size:"<<msg.size()<<std::endl;
std::string results="response from the server:["+msg+"]";
send(new_socket, results.c_str(), results.length(), 0);
//usleep(10*1000);
}
if (close(new_socket)<0){
std::cout <<"close error"<<std::endl;
}
shutdown(server_fd, SHUT_RDWR);
}
} ;
}
void operation(int client_id) {
auto obj = new mySocket::Memcached();
for (int i=0; i<10;i++){
int id=client_id*100+i;
std::cout<<obj->sendMessage(std::to_string(id), "127.0.0.1", 7111)<<std::endl<<std::endl;
}
}
int main(int argc, char const* argv[]) {
// start a socket server
mySocket::Memcached::controller();
// start 3 socket clients
std::thread t1(operation, 1);
std::thread t2(operation, 2);
std::thread t3(operation, 3);
t1.join();
t2.join();
t3.join();
}
In the code above, the client always sends a message with a length of 3. However, the server can receive messages with a length of 0 which causes further errors.
I'm struggling with this for several days and can't figure out why it happens. I noticed
if I add a short sleep inside the server while loop, the problem is solved. (uncomment usleep(10*1000); in the code).
or if I only use one client, the problem is also solved.
Any thought helps.
You are using TCP sockets. You may want to use some application-level protocol like HTTP, websockets instead, that will be much easier, because you will not need to worry about how message is sent/received and in which sequence. If you have to stick with TCP sockets, you firstly have to understand few things:
There's two types of TCP sockets you can use: non-blocking and blocking IO (input/output). You are currently using blocking IO. That IO will be sometimes blocked and you won't be able to do anything with sockets. In blocking IO, it can be work arounded by using one socket per thread on server-side. It's not efficient, but it's relatively easy comparing to Non-blocking IO. Non-blocking IO doesn't wait for anything. While in blocking IO you wait for data, in non-blocking IO you create something like events, callbacks, that are used when there's some data. You probably have to read about these types of IO.
In your server function, would be better, if you listen for incoming connections in one thread, and when there's incoming connection, move this connection into another thread and function, that will handle other things. This may solve your problem related to multiple clients at the same time.
In function operation, instead of allocating memory using raw pointer, use static allocation or smart pointers to avoid memory leaks. If you don't want to, then at least, do delete obj; in the end of function.
And the last one thing. You can use some TCP socket wrapper like sockpp to make things a lot easier. You will have anything TCP sockets have, but in C++ style and a little bit easier to understand and maintain. If you can't use application-level protocol, I strongly suggest you to use some wrapper at least.
Update
As was stated by commenters, there are more things you need to know:
TCP sockets are streams. This means that if you send your message with length of 1024 bytes, it can be divided into several TCP data packets and you can't know if it will be divided or not, how much packets other side will receive etc. You have to read in a while loop using recv() and wait for data. There's some tricks which can help you to properly receive data:
You can send length of your message first, so other side will know how much bytes it needs to receive.
You can place some terminating symbol or sequence of terminating symbols in the end of your message and read until these will be received. This can be a little risky, because there's chance that you would not receive these symbols at all and will be reading next.
You have to join client threads only when you know, that server is already started and listening for incoming connections. You can use some variable as a flag for these purposes, but make note, that you have to pay a lot of attention, when reading/writing variable from two or more different threads. For these purposes, you can use mutexes, which are some mechanism that will allow you safely access one variable from several threads.
Can anyone help me, how to change the below code to non-blocking
struct sockaddr_un server_address;
int server_len, err;
int ret = 1;
int ipc_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (ipc_sockfd < 0) {
printf("%s\n","SHM_IPC: socket creation failed");
return 0;
}
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, SHM_IPC_SOCKET_NAME);
server_len = sizeof(server_address);
err = connect(ipc_sockfd, (struct sockaddr*)&server_address,
server_len);
if (err < 0) {
printf("%s %d\n", "IPC socket server not ready for"
". Try after few moments, Errno:", errno);
close(ipc_sockfd);
return 0;
}
err = write(ipc_sockfd, (void *)msg, sizeof(shm_ipc_msg));
if (err <=0) {
printf("%s %d\n", "SHM_IPC: socket write failed:",errno);
ret = 0;
}
close(ipc_sockfd);
return ret;
This is my client side socket, i need my client side write() and connect() to be non-blocking (I dont care about server side), do I need to change the server socket also to non-blocking to take effect?
Really appreciate your help!
This thread might help you: Does connect() block for TCP socket?
You can make your connect non-blocking but for what reason? if the client is not connected to the server you cannot expect socket.write to work. You need to wait until the connection it's done. The write operation though it is not a blocking operation. If the connection is established the write function should return immediately after sending your data.
I started to lock into socket programming an got into a little trouble:
I created the small program below which sends a message via udp an receives one if possible in a loop. I want to try that with multiple sockets later on, so I use select().
When I use my 127.0.0.1, select() gives a timeout in the first loop (after send()) but after that it always returns 1 indicating that the socket is readable without receiving a message:
//C++
WSADATA wsa;
SOCKADDR_IN RemoteAddr;
SOCKADDR_IN OwnAddr;
SOCKET UDP_Socket1;
fd_set m_Fds;
struct timeval m_Timeout;
int iRemoteAddrLenght = sizeof(SOCKADDR_IN);
int i = 0;
//--Init
WSAStartup (MAKEWORD (2,2), &wsa);
UDP_Socket1 = socket(AF_INET, SOCK_DGRAM, 0);
m_Timeout.tv_sec = 2;
m_Timeout.tv_usec = 0;
RemoteAddr.sin_family = AF_INET;
RemoteAddr.sin_port = htons (2002);
RemoteAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
OwnAddr.sin_family = AF_INET;
OwnAddr.sin_port = htons (2003);
OwnAddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(UDP_Socket1, (SOCKADDR*) &OwnAddr, sizeof(OwnAddr));
for(;;)
{
//..//
//--send
sendto(UDP_Socket1, sSend.c_str(), strlen(sSend.c_str()), 0 , (SOCKADDR*)&RemoteAddr, sizeof(RemoteAddr));
//--select & recv
FD_ZERO(&m_Fds);
FD_SET(UDP_Socket1,&m_Fds);
i = select(sizeof(m_Fds)*8, &m_Fds, NULL, NULL, &m_Timeout);
if(i > 0)
{
recvfrom(UDP_Socket1, m_szBuff, 256, 0, (SOCKADDR*) &RemoteAddr, &m_iRemoteAddrLenght);
} //if
else if(i < 1) // "0" in 1st loop, then "1" =(
{
cout << "Udp Timeout" << endl;
} //else if
} //for
send returns 56 (bytes send)
with WSAGetLastError: 0
recvfrom returns -1
with WSAGetLastError: 10054
I'd appreciate your help about why select() returns 1 when it should timeout
You can ignore these kinds of errors for UDP. Some operating systems report them, some don't. They're basically meaningless.
Rant: I really dislike boost::asio, So I've been looking at alternatives and came across libev. Which seems simple enough for me, but is doing a few things I cannot understand. If those are too many questions in one thread, please let me know.
1) I set the listening socket to NON_BLOCK, I also set each accepted incoming connection as NON_BLOCK, yet somewhere in the code the socket(s) turns into BLOCK.
Ex:
bool Server::Start()
{
// Setup event loop
loop = ev_default_loop(EVBACKEND_SELECT); //EVFLAG_AUTO ?
// Create Socket
sockfd = socket(PF_INET, SOCK_STREAM, 0);
addr_len = sizeof(addr)
// Set Socket to non blocking
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK);
if (fcntl(sockfd, F_GETFL) & O_NONBLOCK) std::cout << "Socket is NONBLOCK" << std::endl;
else std::cout << "Socket is BLOCK" << std::endl;
if (sockfd < 0) {
std::cout << "ERROR opening socket" << std::endl;
return false;
}
bzero((char *)&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
// Bind port to socket
if (bind(sockfd,(struct sockaddr*)&addr, sizeof(addr))!=0) {
std::cout << "bind error" << std::endl;
return false;
}
// Listen
if (listen(sockfd, 2) < 0) {
std::cout << "listen error" << std::endl;
return false;
}
// Initialize and start a watcher to accepts client requests
ev_io_init(&w_accept, accept_cb, sockfd, EV_READ);
ev_io_start(loop, &w_accept);
return true;
}
I have tried to make the main loop also not to block:
void Server::MainLoop()
{
// Start infinite loop
while (1) {
ev_loop(loop, EVLOOP_NONBLOCK);
}
}
But it doesnt seem to have made a different. PLEASE DO NOT redirect me to the documentation (the only available source of documentation on the internet) I have read it.
I do this for the client socket that has been accepted:
void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
....
c->client_sd = accept(watcher->fd, (struct sockaddr *)&c->client_addr, &c->client_len);
....
ev_io *w_client = (struct ev_io*) malloc (sizeof(struct ev_io));
ev_io_init(w_client, read_cb, c->client_sd, EV_READ);
ev_io_start(loop, w_client);
fcntl(watcher->fd, F_SETFL, fcntl(watcher->fd, F_GETFL) | O_NONBLOCK);
Yet every time my read callback is executed, the socket is magically set to BLOCK
2) I have tried setting a timeout for the socket:
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
if (setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
sizeof(timeout)) < 0)
error("setsockopt failed\n");
if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
sizeof(timeout)) < 0)
error("setsockopt failed\n");
(Taken from here: this question)
It simply doesn't work. Is this because the sockets are reset to BLOCKing mode ?
3) I have seen a C++ wrapper for libev. I absolutely hate the fact I have to make the callbacks static functions, it ruins everything for me. Yet all the examples I have seen use:
signal.loop.break_loop();
and
loop.run(0);
which, funnily enough produces:
error: ‘struct ev::loop_ref’ has no member named ‘break_loop’ error:
‘struct ev::default_loop’ has no member named ‘run’
on Debian Squeeze.
So, what I am asking is:
What, who, where is the socket changed from NON_BLOCK to BLOCK ?
How (if) can I set a timeout for the socket (blocking or non-blocking)
What is wrong with ev++.h and why are those nice people using the wrappers I can't use?
Please, bear in mind that I can use the sockets to read and send data, but in a blocking manner, without timeouts. Furthermore, as this is a server, I NEED to keep the code in classes, as I have to save messages per connected clients. Making this static or non-class methods simply ruins it, or forces me to take a very different approach.
PS: Any alternatives to libev ?
You aren't setting the client FD to non-blocking mode. You are setting the listening socket FD.
I am trying to use non-blocking TCP sockets. The problem is that they are still blocking. The code is below -
server code -
struct sockaddr name;
char buf[80];
void set_nonblock(int socket) {
int flags;
flags = fcntl(socket,F_GETFL,0);
assert(flags != -1);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);
}
int main(int agrc, char** argv) {
int sock, new_sd, adrlen; //sock is this socket, new_sd is connection socket
name.sa_family = AF_UNIX;
strcpy(name.sa_data, "127.0.0.1");
adrlen = strlen(name.sa_data) + sizeof(name.sa_family);
//make socket
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
printf("\nBind error %m", errno);
exit(1);
}
//unlink and bind
unlink("127.0.0.1");
if(bind (sock, &name, adrlen) < 0)
printf("\nBind error %m", errno);
//listen
if(listen(sock, 5) < 0)
printf("\nListen error %m", errno);
//accept
new_sd = accept(sock, &name, (socklen_t*)&adrlen);
if( new_sd < 0) {
cout<<"\nserver accept failure "<<errno;
exit(1);
}
//set nonblock
set_nonblock(new_sd);
char* in = new char[80];
std::string out = "Got it";
int numSent;
int numRead;
while( !(in[0] == 'q' && in[1] == 'u' && in[2] == 'i' && in[3] == 't') ) {
//clear in buffer
for(int i=0;i<80;i++)
in[i] = ' ';
cin>>out;
cin.get();
//if we typed something, send it
if(strlen(out.c_str()) > 0) {
numSent = send(new_sd, out.c_str(), strlen(out.c_str()), 0);
cout<<"\n"<<numSent<<" bytes sent";
}
numRead = recv(new_sd, in, 80, 0);
if(numRead > 0)
cout<<"\nData read from client - "<<in;
} //end while
cout<<"\nExiting normally\n";
return 0;
}
client code -
struct sockaddr name;
void set_nonblock(int socket) {
int flags;
flags = fcntl(socket,F_GETFL,0);
assert(flags != -1);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);
}
int main(int agrc, char** argv) {
int sock, new_sd, adrlen;
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
printf("\nserver socket failure %m", errno);
exit(1);
}
//stuff for server socket
name.sa_family = AF_UNIX;
strcpy(name.sa_data, "127.0.0.1");
adrlen = strlen(name.sa_data) + sizeof(name.sa_family);
if(connect(sock, &name, adrlen) < 0) {
printf("\nclient connection failure %m", errno);
exit(1);
}
cout<<"\nSuccessful connection\n";
//set nonblock
set_nonblock(sock);
std::string out;
char* in = new char[80];
int numRead;
int numSent;
while(out.compare("quit")) {
//clear in
for(int i=0;i<80;i++)
in[i] = '\0';
numRead = recv(sock, in, 80, 0);
if(numRead > 0)
cout<<"\nData read from server - "<<in;
cout<<"\n";
out.clear();
cin>>out;
cin.get();
//if we typed something, send it
if(strlen(out.c_str())) {
numSent = send(sock, out.c_str(), strlen(out.c_str()), 0);
cout<<"\n"<<numSent<<" bytes sent";
}
} //end while
cout<<"\nExiting normally\n";
return 0;
}
Whenever I run it, the server still waits for me to send something before it will read and output what the client has sent. I want either the server or client to be able to send the message as soon as I type it, and have the other read and output the message at that time. I thought non-blocking sockets was the answer, but maybe I am just doing something wrong?
Also, I was using a file instead of my 127.0.0.1 address as the sockaddr's data. If that is not how it should be properly used, feel free to say so (it worked how it worked previously with a file so I just kept it like that).
Any help is appreciated.
General approach for a TCP server where you want to handle many connections at the same time:
make listening socket non-blocking
add it to select(2) or poll(2) read event set
enter select(2)/poll(2) loop
on wakeup check if it's the listening socket, then
accept(2)
check for failure (the client might've dropped the connection attempt by now)
make newly created client socket non-blocking, add it to the polling event set
else, if it's one of the client sockets
consume input, process it
watch out for EAGAIN error code - it's not really an error, but indication that there's no input now
if read zero bytes - client closed connection, close(2) client socket, remove it from event set
re-init event set (omitting this is a common error with select(2))
repeat the loop
Client side is a little simpler since you only have one socket. Advanced applications like web browsers that handle many connections often do non-blocking connect(2) though.
Whenever I run it, the server still waits for me to send something before it will read and output what the client has sent.
Well, that is how you wrote it. You block on IO from stdin, and then and only then do you send/receive.
cin>>out;
cin.get();
Also, you are using a local socket (AF_UNIX) which creates a special file in your filesystem for interprocess communication - this is a different mechanism than IP, and is definitely not TCP as you indicate in your question. I suppose you could name the file 127.0.0.1, but that really doesn't make sense and implies confusion on your part, because that is an IP loopback address. You'll want to use AF_INET for IP.
For an excellent starter guide on unix networking, I'd recommend http://beej.us/guide/bgnet/
If you want the display of messages received to be independant of your cin statements, either fork() off a seperate process to handle your network IO, or use a separate thread.
You might be interested in select(). In my opinion non-blocking sockets are usually a hack, and proper usage of select() or poll() is generally much better design and more flexible (and more portable). try
man select_tut
for more information.
I think you have to set non-block sooner (ie get the socket then set it non block)
also check that the fcntl to set it actually worked
If you want non-blocking i/o, you want to use select. You can set it with stdin as one of the sockets it is listening on, along with the client sockets (just add file descriptor 1, which is stdin, to the fd_set).
http://beej.us/guide/bgnet/output/html/multipage/advanced.html
I would recommend reading through what beej has to say about select. It looks a little intimidating but is really useful and simple to use if you take a little time to wrap your head around it.