I am trying to use ZMQ socket on my Ubuntu machine to communicate with a ESP8266 edge device. I tried this piece of Python code which works fine:
import zmq
ctx = zmq.Context()
router = ctx.socket(zmq.ROUTER)
router.router_raw = True
router.bind("tcp://*:8081")
while True:
msg = router.recv_multipart()
identity, body = msg
print(identity)
print(body)
as it gives (server side)
b'\x00k\x8bEg'
b''
b'\x00k\x8bEg'
b'hello from ESP8266'
b'\x00k\x8bEg'
b'\r\n'
but when I translate it into C++ as
#include <zmq_addon.hpp>
int main () {
zmq::context_t context;
zmq::socket_t socket(context, zmq::socket_type::router);
int router_raw = 1;
zmq_setsockopt(&socket, ZMQ_ROUTER_RAW, &router_raw, 1);
socket.bind("tcp://*:8081");
while (true) {
std::cout << "listening " << std::endl;
std::vector<zmq::message_t> msgs;
if (zmq::recv_multipart(socket, std::back_inserter(msgs))) {
std::cout << "got " << static_cast<const char *> (msgs.front().data())
<< std::endl;
}
}
return 0;
}
it doesn't work any more and hangs before recv_multipart, though at the same time ESP8266 client do recieve some wierd βΈ® symbol which indicates tcp connection success I guess.
The weird symbols that the client receives are ZMQ's internal protocol, which ZMQ_ROUTER_RAW normally suppresses, except that your call to set ZMQ_ROUTER_RAW is wrong. The last parameter to zmq_setsockopt is supposed to be the size in bytes of the new option value you are passing. It should be sizeof(router_raw) instead of 1.
Related
I am currently working on a project that requires me to connect two terminals via ZMQ sockets, and my current solution does so via the PUB-SUB Socket functionality. However, when I run the programs, while the publisher sends the messages, the subscriber never receives any of the messages. I've tried changing the IP address between them, and trying to "brute force send" message between the sub and the pub, but to no avail.
Reduced form of the code:
Server.cpp:
#include <zmq.h>
const char* C_TO_S = "tcp://127.0.0.1:5557";
const char* S_TO_C = "tcp://127.0.0.1:5558";
int main() {
zmq::context_t context(1);
zmq::socket_t pub(context, ZMQ_PUB);
zmq::socket_t sub(context, ZMQ_SUB);
int sndhwm = 0;
sub.connect(C_TO_S);
pub.bind(S_TO_C);
sub.setsockopt(ZMQ_SUBSCRIBE, &sndhwm, sizeof(sndhwm));
//cout << C_TO_S << endl;
while(true) {
zmq::message_t rx_msg;
sub.recv(&rx_msg);
cout << "b\n";
// other code goes here
}
}
Client.cpp:
#incldue <zmq.h>
const char* C_TO_S = "tcp://127.0.0.1:5557";
const char* S_TO_C = "tcp://127.0.0.1:5558";
void network_thread() {
zmq::context_t context(1);
zmq::socket_t pub(context, ZMQ_PUB);
zmq::socket_t sub(context, ZMQ_SUB);
int sndhwm = 0;
sub.connect(S_TO_C);
pub.connect(C_TO_S);
sub.setsockopt(ZMQ_SUBSCRIBE, &sndhwm, sizeof(sndhwm));
while (true) {
cout << pub.send("a", strlen("a"), 0);
cout << "AA\n";
}
// Other code that doesn't matter
}
The main in Client.cpp calls network_thread in a separate thread, and then spams the publisher to send the message "a" to the server. However, the server does not get any message from the client. If the server got any message, it would print out "b", however it never does that. I also know that the publisher is sending messages because it prints out "1" upon execution.
Also, assume that the client subscriber and the server publisher has a purpose. While they don't work atm either, a fix to the other set should translate into a fix of those.
I have tried changing the port, spamming send messages, etc. Nothing resulted in the server receiving any messages.
Any help would be appreciated, thank you.
You set a message filter option on the SUB socket. This means that you will only receive messages that begin with the bytes set by the filter.
This code:
int sndhwm = 0;
sub.setsockopt(ZMQ_SUBSCRIBE, &sndhwm, sizeof(sndhwm));
Sets the filter to sizeof(sndhwm) bytes with value 0x00. But your message does not begin with this number of 0x00 bytes. Hence the message is ignored by the SUB socket.
You should remove the setsockopt call.
If your intent was to clear the message filter, you can do this with:
sub.setsockopt(ZMQ_SUBSCRIBE, nullptr, 0);
I'm working on a project that is relying fairly heavily on packet latency. The architect for the overall system wants to use the Poco::Net::Websocket protocol as the transport layer between the different nodes. We have been running the application with ZMQ till this point, but there's too much overhead and we are not seeing the speeds we need. So I'm tasked with converting the ZMQ portion of the system over to WebSockets.
I've never used WebSockets before and I'm having issues understanding what's going on. I know WebSockets were originally designed as a way to speed up communication between the web browser and the page server decreasing latency through the use of full-duplex bi-directional communication.
There are a lot of tutorials for javascript, nodejs, and even python, but it's more limited when you're talking about C++, which is what I'm working with. The examples that I have found are geared more towards browser requests, which I'm not surprised by. Rather than general TCP packet transfer, which is what our use case would be.
Here are my thoughts and questions.
Seeing how I couldn't really understand what was happening, I decided to start with a Poco::Net::SocketStream configuration first. I chose to do it this way, based on the thought that Websockets are nothing more than a TCP socket inserted into a higher-level protocol framework. I figured transitioning would be easier than trying from scratch. So I spun up a server and client application using standard Poco socket libraries.
I used the example here as a base. I just ended up moving everything to a single file. I also spun up a Poco::Net::TCPServer as well, a sample found here.
I modified both to manage the fact that I'm sending JSON packets between the client and server. I took snippets of our original code to format the JSON packets and encode them with CBOR. I ended up keeping the zmq::message_t format that I was originally using, as I ran into issues receiving the packets on the other end. So as it wasn't the major issue I just skipped that part for now. So I'm aware the ZMQ portions of the code below aren't necessary, a small problem for another time.
Here is the Client code for reference.
#include "Poco/Net/HTTPRequest.h"
#include "Poco/Net/HTTPResponse.h"
#include "Poco/Net/HTTPMessage.h"
#include "Poco/Net/WebSocket.h"
#include "Poco/Net/HTTPClientSession.h"
#include <iostream>
#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/SocketStream.h"
#include "Poco/Net/WebSocket.h"
#include <jsoncons/json.hpp>
#include <jsoncons/json.hpp>
#include <jsoncons_ext/cbor/cbor.hpp>
#include <jsoncons_ext/jsonpath/json_query.hpp>
#include <jsoncons_ext/jsonpath/jsonpath.hpp>
#include <iostream>
#include <string>
#include <zmq.hpp>
using namespace jsoncons;
using Poco::Net::HTTPClientSession;
using Poco::Net::HTTPMessage;
using Poco::Net::HTTPRequest;
using Poco::Net::HTTPResponse;
using Poco::Net::WebSocket;
class ClientHandler
{
private:
std::string host;
int port;
// IP endpoint/socket address (consists of host addr and port #)
Poco::Net::SocketAddress socketAddr;
// Interface to a TCP stream socket
// Poco::Net::StreamSocket socket;
Poco::Net::Socket socket;
// Stream for reading from / writing to a socket (accepts a socket)
Poco::Net::SocketStream stream;
public:
ClientHandler(std::string h, int p) : host(h), port(p), socketAddr(h, p), socket(), stream(socket)
{
std::cout << "Host: " << host << "\tPort: " << port << std::endl;
}
// Connect to the initialized socket address' hostname and port
bool connected()
{
std::cout << "Creating a connection with ["
<< socketAddr.host().toString()
<< "] through port [" << socketAddr.port() << "] ...";
try
{
// socket.connect(socketAddr);
// std::cout << "Success!" << std::endl;
}
catch (Poco::Exception err)
{
std::cout << std::endl;
std::cout << "Socket connection error: [" << err.displayText() << "]" << std::endl;
return false;
}
return true;
}
// Send a message to the connected server
bool sendMessage()
{
std::cout << std::endl;
const char channel[] = "fool";
std::string channelPort = std::to_string(5577);
try
{
std::string message;
std::cout << "Enter a message to send to the server: ";
std::cin >> message;
json msg = json(json_object_arg, {{"$op", "create_channel"}, {"$svc", "zmq"}, {"channel_name", channel}, {"port", channelPort}});
std::vector<uint8_t> buffer;
jsoncons::cbor::encode_cbor(msg, buffer);
zmq::const_buffer msg_out = zmq::buffer(buffer);
if (message.compare("exit") != 0)
{
std::cout << "Sending the message to the server!\n\t";
// socket.sendBytes(msg_out.data(), msg_out.size());
return true;
}
else
return false;
}
catch (Poco::Exception err)
{
std::cout << "Data send error: [" << err.displayText() << "]" << std::endl;
return false;
}
}
};
int main(int argc, char **argv)
{
int port = 2001;
std::string hostname = "10.0.12.97";
// Handle the server-client connection and send some JSON
try
{
ClientHandler handler(hostname, port);
if (handler.connected())
while (handler.sendMessage())
;
}
catch (Poco::Exception err)
{
std::cout << "Handler Error -> " << err.displayText() << std::endl;
}
return 0;
}
So, How do I get from this ^ to Websockets?
I have been reading and digging through the documentation for Poco Websockets. At first, I thought I needed to figure out the HTTPClientSession, HTTPRequest, and HTTPResponse objects I saw in so many examples. However, looking at Documentation for Poco Websockets constructors, it appeared all I needed to do was pass it a socket connection. So spent some time trying to figure that out, but after going back to the documentation it appears that it specifies that I need to send it a socket that is already a Websocket.
Creates a WebSocket from another Socket, which must be a WebSocket,
That's what I get for not actually reading it. So I spent some more time reading through the constructors and it appears that I do in fact have to send at least 3 arguments.
WebSocket
WebSocket(
HTTPClientSession & cs,
HTTPRequest & request,
HTTPResponse & response
);
Creates a client-side WebSocket, using the given HTTPClientSession and HTTPRequest for the initial handshake (HTTP Upgrade request).
So I'm now back to HTTPClientSession, HTTPRequest, and HTTPResponse objects.
I think I understand HTTPClientSession. Here I should provide the Server name and port. So updating the code above should look something like this
...
Poco::Net::HTTPClientSession cs;
public:
ClientHandler(std::string h, int p) : host(h), port(p), cs(h, p)
...
HTTPResponse looks to be a pointer to a response object for the WebSocket to return the HTTPRequest response. Looks to contain a status code, date, reason(not sure what reason is)
The HTTPRequest is a point of interest. I'm lost as to what I need to format this as. There are three arguments for the request.
Method - this looks like nothing more than telling the server that you are requesting something or looking to hand something over.
HTTP_POST - I would assume this is what I want?
HTTP_GET
URI - for HTTP_GET this would be the path of the resource I'm looking to retrieve. And for Post, this would be the path that I would like to post something to on the server
Version - looks to be self-explanatory, as this is the HTTP version I would like to use.
but here is the rub of it, I do not want to post/get anything that's associated with a URI, I'm just looking to transfer a JSON packet and manage it on the other end. I've found a sample code of a client that uses this information, but I don't understand why it's using HTTP_GET, and the reason for the /?encode=text reference. I can't seem to find details anywhere on it. That leads me to the set value of the request object, don't understand what the "set()" call is for or how it would work in my situation.
With the new information, I updated my constructor to this.
...
Poco::Net::WebSocket *ws;
public:
ClientHandler(std::string h, int p) : host(h), port(p)
{
std::cout << "TEST" << std::endl;
// Poco::Net::HTTPClientSession cs(h, p);
Poco::Net::HTTPClientSession cs(host, port);
Poco::Net::HTTPRequest req(HTTPRequest::HTTP_GET, "/?encoding=text", HTTPMessage::HTTP_1_1);
// req.set("origin", "http://www.websocket.org");
Poco::Net::HTTPResponse resp;
ws = new WebSocket(cs, req, resp);
std::cout << "Host: " << host << "\tPort: " << port << std::endl;
}
...
However, I enter the constructor but never leave it. It gets hung up when I attempt to create the WebSocket. I'm assuming it doesn't like something. I tried the HTTPClientSesseion cs two ways. and leaving the req.set() in as well. Just hangs.
Could someone lend me some insight as to how I can get Poco::Net::Websocket running? and some information so I can understand what's going on a bit better. So when I attempt the server-side I will have some more information that will help.
I have one machine running simultaniously some C++ application and a Node.js server.
Use-case:
I want to be able to trigger my C++ application and make it pass some data (lets say a string) into a socket file. Then my Node.js server shall fetch that data from the socket and print it on some web page via a TCP-port (Code not included here/yet). The same should happen the other way around.
What I've done so far:
I was able to write some strings from my Node.js server into to the socket file with the following code:
server.js
var net = require('net');
var fs = require('fs');
var socketPath = '/tmp/sock';
fs.stat(socketPath, function(err) {
if (!err) fs.unlinkSync(socketPath);
var unixServer = net.createServer(function(localSerialConnection) {
localSerialConnection.on('data', function(data) {
// data is a buffer from the socket
console.log('Something happened!');
});
// write to socket with localSerialConnection.write()
localSerialConnection.write('HELLO\n');
localSerialConnection.write('I\'m\n');
localSerialConnection.write('DOING something!\n');
localSerialConnection.write('with the SOCKS\n');
});
unixServer.listen(socketPath);
});
reading the content with nc -U /tmp/sock and with the following output https://i.stack.imgur.com/ye2Dx.png.
When I run my C++ code:
cpp_socket.cpp
#include <boost/asio.hpp>
#include <iostream>
int main() {
using boost::asio::local::stream_protocol;
boost::system::error_code ec;
::unlink("/tmp/sock"); // Remove previous binding.
boost::asio::io_service service;
stream_protocol::endpoint ep("/tmp/sock");
stream_protocol::socket s(service);
std::cout << "passed setup section" << std::endl;
s.connect(ep);
std::cout << "passed connection" << std::endl;
std::string message = "Hello from C++!";
std::cout << "before sending" << std::endl;
boost::asio::write(s, boost::asio::buffer(message), boost::asio::transfer_all());
/* s.write_some(boost::asio::buffer("hello world!"), ec); */
std::cout << "after sending" << std::endl;
I get the following output:
/cpp_socket
passed setup section
terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
what(): connect: No such file or directory
Aborted (core dumped)
Even though the /tmp/sock file still exists.
When I remove ::unlink("/tmp/sock"); // Remove previous binding. with comments it runs through, but my Node.js server stops running and nc -U /tmp/sock looses its connection.
Neither the .write() nor the .write_some() function seems to work.
I assume that I miss something trivial or I'm not following basic concepts of unix socket communication.
Questions:
Is it even possible to listen with one Node.js server application to a TCP-port and a UNIX-socket at the same time?
Am I understanding the concept of unix socket communication correctly, judging from my input?
How can I read or write from C++ from/into a socket, preferably with C++ boost/asio library. But not necessarily necessary :-)
Am I asking the right questions?
As you might see, I'm not too experienced with these subjects. If I haven't addressed my issues accordingly and not precisely enough,it's due to my lack of experience.
Thanks a lot in advance. Lets have a fruitful discussion.
Oh oops. The error was in plain sight:
::unlink("/tmp/sock"); // Remove previous binding.
Removes the socket. That's not good if you wanted to connect to it.
Removing that line made it work:
passed setup section
passed connection: Success
before sending
after sending
And on the listener side:
Which is, I guess, to be expected because the client isn't complete yet.
Disclaimer:
I made it work with TCP sockets, but I would like to see how its possible with unix sockets. One more open port could lead to potential security threats (correct me if I'm wrong). So if you (sehe) or someone knows how to achieve this, please feel free to share. Since I wasn't able to find this in my searches over the internet, it could be helpful for others, too.
What I did now:
Creating a NodeJS server which is listening to two ports. One port for the web-browser and one for the C++ application
Connect the C++ application with one port
Sending strings using telnet
server.js
const net = require('net');
const express = require('express');
const app = express();
const c_port = 6666;
const si_port = 8888;
//------------- From here Browser stream is handled -------------//
app.get('/', (req, res)=>{
res.send('Hello from Node!');
});
app.get('/index.html', (req, res) => {
res.sendFile(__dirname + "/" + "index.html");
});
app.listen(si_port,(req, res)=>{
console.log(`Listening on http://localhost:${si_port}`);
});
//------------- From here C++ stream is handled -------------//
var server = net.createServer(function(c) { //'connection' listener
console.log('client connected');
c.on('end', function() {
console.log('client disconnected');
});
c.write('hello\r\n');
c.on('data', function(data){
var read = data.toString();
console.log(read);
// var message = c.read();
// console.log(message);
})
// c.pipe(c);
c.write('Hello back to C++'); // But only if you shut down the server
});
server.listen(c_port, function() { //'listening' listener
console.log(`Listening for input from C++ application on port:${c_port}`);
});
client.cpp
#include <iostream>
#include <boost/asio.hpp>
int main(int argc, char* argv[])
{
if(argc != 4){
std::cout<<"Wrong parameter\n"<<"Example usage ./client 127.0.0.1 1234 hello"<<std::endl;
return -1;
}
auto const address = boost::asio::ip::make_address(argv[1]);
auto const port = std::atoi(argv[2]);
std::string msg = argv[3];
msg = msg + '\n';
boost::asio::io_service io_service;
//socket creation
boost::asio::ip::tcp::socket socket(io_service);
//connection
boost::system::error_code ec;
socket.connect( boost::asio::ip::tcp::endpoint( address, port ),ec);
if(ec){std::cout<<ec.message()<<std::endl; return 1;}
// request/message from client
//const string msg = "Hello from Client!\n";
boost::system::error_code error;
boost::asio::write( socket, boost::asio::buffer(msg), error );
if(error){
std::cout << "send failed: " << error.message() << std::endl;
}
// getting response from server
boost::asio::streambuf receive_buffer;
boost::asio::read(socket, receive_buffer, boost::asio::transfer_all(), error);
if( error && error != boost::asio::error::eof ){
std::cout << "receive failed: " << error.message() << std::endl;
}
else{
const char* data = boost::asio::buffer_cast<const char*>(receive_buffer.data());
std::cout << data << std::endl;
}
return 0;
}
With telnet localhost 6666 I can easily on that port and send random strings.
Executing my binary with additional arguments and a string I was able to send some data from my C++: ./clientcpp 127.0.0.1 6666 "HELLO from C++". And here is the output:
Thanks a lot again.
I have a pretty basic live555 RTSP server and client to stream a h264 stream written in c++.
Here's the code I have for the client (adapted from testProgs/testRTSPClient.cpp, bundled with live555)
client->scheduler = BasicTaskScheduler::createNew();
client->env = BasicUsageEnvironment::createNew(*client->scheduler);
client->rtspClient = NULL;
RTSP_CLIENT::eventLoopWatchVariable = 0;
openURL(client, *client->env, string(string("rtsp://") + ip_address + ":" + to_string(BASE_RTSP_PORT + iris_id) + "/iris").c_str());
client->env->taskScheduler().doEventLoop(&RTSP_CLIENT::eventLoopWatchVariable);
void openURL(RTSP_CLIENT* client, UsageEnvironment& env, char const* rtspURL) {
// Begin by creating a "RTSPClient" object. Note that there is a separate "RTSPClient" object for each stream that we wish
// to receive (even if more than stream uses the same "rtsp://" URL).
while (!client->rtspClient) {
client->rtspClient = ourRTSPClient::createNew(env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, "main");
}
// Next, send a RTSP "DESCRIBE" command, to get a SDP description for the stream.
// Note that this command - like all RTSP commands - is sent asynchronously; we do not block, waiting for a response.
// Instead, the following function call returns immediately, and we handle the RTSP response later, from within the event loop:
client->rtspClient->sendDescribeCommand(continueAfterDESCRIBE);
}
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString) {
do {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
if (resultCode != 0) {
env << *rtspClient << "Failed to get a SDP description: " << resultString << "\n";
delete[] resultString;
break;
}
char* const sdpDescription = resultString;
env << *rtspClient << "Got a SDP description:\n" << sdpDescription << "\n";
// Create a media session object from this SDP description:
scs.session = MediaSession::createNew(env, sdpDescription);
delete[] sdpDescription; // because we don't need it anymore
if (scs.session == NULL) {
env << *rtspClient << "Failed to create a MediaSession object from the SDP description: " << env.getResultMsg() << "\n";
break;
} else if (!scs.session->hasSubsessions()) {
env << *rtspClient << "This session has no media subsessions (i.e., no \"m=\" lines)\n";
break;
}
// Then, create and set up our data source objects for the session. We do this by iterating over the session's 'subsessions',
// calling "MediaSubsession::initiate()", and then sending a RTSP "SETUP" command, on each one.
// (Each 'subsession' will have its own data source.)
scs.iter = new MediaSubsessionIterator(*scs.session);
setupNextSubsession(rtspClient);
return;
} while (0);
// An unrecoverable error occurred with this stream.
shutdownStream(rtspClient);
}
Here's the code I have for the server (adapted from testProgs/testOnDemandRTSPServer.cpp, bundled with live555)
rtsp_server->taskSchedular = BasicTaskScheduler::createNew();
rtsp_server->usageEnvironment = BasicUsageEnvironment::createNew(*rtsp_server->taskSchedular);
rtsp_server->rtspServer = RTSPServer::createNew(*rtsp_server->usageEnvironment, BASE_RTSP_PORT + iris_id, NULL);
rtsp_server->eventLoopWatchVariable = 0;
if(rtsp_server->rtspServer == NULL) {
*rtsp_server->usageEnvironment << "Failed to create rtsp server ::" << rtsp_server->usageEnvironment->getResultMsg() <<"\n";
return false;
}
rtsp_server->sms = ServerMediaSession::createNew(*rtsp_server->usageEnvironment, "iris", "iris", "stream");
rtsp_server->liveSubSession = H264LiveServerMediaSession::createNew(*rtsp_server->usageEnvironment, true);
rtsp_server->sms->addSubsession(rtsp_server->liveSubSession);
rtsp_server->rtspServer->addServerMediaSession(rtsp_server->sms);
rtsp_server->taskSchedular->doEventLoop(&rtsp_server->eventLoopWatchVariable);
I was under the assumption that live555 by default used UDP to transport data to the client from the server, which is what I wanted for it's latency benefits over TCP. However while running the server client I happened to check netstat and I found this:
~# netstat | grep 8554
tcp 0 0 x.x.x.x:8554 wsip-x-x-x-x:39224 ESTABLISHED
It is however showing that the communications are going through TCP not UDP. I am a bit confused here, am I mis-interpreting netstat here?
Is there anything I need to tune in my c++ code to force the communication to go through UDP not TCP?
Okay so I figured out the answer. To help anyone else who is curious about this, the code is actually all correct. There is also no mis-interpretation of netstat. RTSP does indeed run over TCP not UDP. However the transport method of the A/V data runs on RTP, a connection that RTSP simply negotiates and instantiates. RTP almost always will run over UDP. To figure out what port and protocol the A/V data stream is going over you will need to sniff the packets sent out via RTSP. In my case the A/V data stream was indeed still going over UDP.
I've programmed a simple c++ client which should communicate with another program using a listener thread (a downloaded EchoServer in Java) over the local loop on Ubuntu 14.04. However, there's a problem in the following situation:
Client connects to server
Server sends greeting message
Client receives message and sends a new message to server
Client waits for an answer; the main thread sleeps and the listener thread waits for an answer using recv()
In the next step, the server should receive the message sent by the server, but it doesn't. Instead it first receives the message once the client terminates.
I think that the problem is that the client blocks resources and thus not allowing the server to receive the message, but I'm not sure. Unfortunately I don't have the option to test this on two separate machines.
Code snippets:
// main method
int main(void) {
Client client("127.0.0.1", 13050);
std::cout << client.open() << std::endl;
client.attachListener(foo);
usleep(1000 * 1000 * 2);
std::cout << client.send("hello") << std::endl;
usleep(1000 * 1000 * 5);
}
// send method
int Client::send(const char* msg) {
return write(sockfd, msg, strlen(msg));
}
// listener function
void* Client::listen() {
char buffer[256];
unsigned int receive_size = 0;
while(true) {
receive_size = 0;
while((receive_size = recv(sockfd, &buffer, 256, 0)) > 0) {
buffer[receive_size] = '\0';
msgHandler(buffer);
bzero(&buffer, 256);
}
if(receive_size == 0) {
msgHandler("Server disconnected");
} else if(receive_size == 1) {
msgHandler("Connection failure!");
}
}
return NULL;
}
Output:
1
Welcome to the Java EchoServer. Type 'bye' to close.
6
The EchoServer implementations typically want to see a newline on the message you send before they'll echo it back. Instead of client.send("hello") try client.send("hello\n").
Also, though this isn't really necessary for an application you're just experimenting with, you might want to turn off the Nagle algorithm on your client socket so that small messages get sent immediately. Add code like this just after the point where you call connect with client socket:
int flag = 1;
int res = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof flag);
if (res < 0) // handle setsockopt failure here...
This code requires these header files:
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>