I am attempting to setup a TCP/IP socket connection between Java and C++, with Java on Windows and C++ on a Raspberry Pi. The message being transmitted is a Google Protocol Buffer message, with a proto set as below:
package package_name;
message Win2Pi{
optional int32 num1= 1;
optional int32 num2= 2;
optional int32 num3= 3;
optional int32 num4= 4;
optional bool logic1= 5;
optional bool logic2= 6;
optional bool logic3= 7;
optional bool logic4= 8;
optional bool logic5= 9;
optional int32 num5= 10;
optional bool logic6= 11;
}
I have the following code for Java (which acts as a client):
/* Java Code to Open Socket, Create Protobuf Message, and Send */
Socket clientSocket = new Socket(hostName, portNumber);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
WinToPi.Builder w2p = WinToPi.newBuilder();
w2p.setNum1(255);
w2p.setNum2(255);
w2p.setNum3(255);
w2p.setNum4(255);
w2p.setLogic1(true);
w2p.setLogic2(true);
w2p.setLogic3(true);
w2p.setLogic4(true);
w2p.setLogic5(false);
w2p.setNum5(7);
w2p.setLogic6(true);
w2p.build().writeTo(clientSocket.getOutputStream());
I have the following code for C++ (which acts as a server):
//Protobuf Setup Variables
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[1024];
struct sockaddr_in serv_addr, cli_addr;
int n;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0){
std::cout << "Error Opening Socket!" << std::endl;
exit(1); //error
}
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0){
std::cout << "Error on Binding!" << std::endl; ;
exit(1); //error
}
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) {
std::cout << "ERROR on accept" << std::endl;
exit(1);
}
/* Clear Buffer and Read Message from Client */
bzero(buffer,1024);
n = read(newsockfd,buffer,1023);
std::cout << "n: " << n << std::endl;
if (n < 0){
std::cout << "Error Reading From Socket!" << std::endl;
}
/* Translate Shoreside to Pi Message */
std::string inputStr = std::string(buffer);
package_name::WinToPi w2p;
w2p.ParseFromString(inputStr);
With a static message, I am able to receive the appropriate number of bytes and values. With this, I moved to having values change dynamically on the Java side. However, it appears that on the c++ side, I will receive the correct number of bytes but the values will not have changed for a majority of the variables (only the first few). When I check the packaged and transmitted Google Protocol Buffer message on the Java side, it appears that I am sending the correct values. Is there a better approach for receiving a Google Protocol Buffer message in c++?
Your biggest problem seems to be this:
std::string inputStr = std::string(buffer);
When you construct a string like that - from a const char* - it will look for the first NUL as the terminator. You should instead use...
std::string inputStr = std::string(buffer, n);
...which will ensure the entire chunk of received data is stored into the string.
Another problem:
read on a socket may return whatever's sent over multiple calls, so you should always adopt a convention for working out when to stop reading (e.g. a fixed number of bytes known to client and sender - perhaps from a structure size, or a sentinel character or sequence such as newline or NUL, or a fixed-sized length prefix)
this is a natural consequence of buffering over the stream: say your client app calls write/send thrice while the OS is too busy to actually get any of the data into a network packet: when it does do so it may be able to fit the first and half the second "write" into one packet, then send the rest of the second along with the third in another; if the receiver expects each read to read the start of a multi-byte logical message, they're in for a shock
As for better approaches, for some small casual use I've found boost::asio makes for very concise, clear code and is a pleasure to use... lots of docs, examples, tutorials online.
Related
I've got a C++ Socket Server that sends a JSON String to a Java Client. For the Java Part I'm using the following Code:
BufferedReader in = new BufferedReader(new InputStreamReader(soc.getInputStream()));
while((inString = in.readLine()) != null) {
Log.i("JSON", inString);
C++ Code:
WSADATA wsa;
SOCKET s, new_socket;
struct sockaddr_in server, client;
int c;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { return false; }
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { return false; }
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(13377);
if (bind(s, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR) { return false; }
listen(s, 3);
c = sizeof(struct sockaddr_in);
new_socket = accept(s, (struct sockaddr *)&client, &c);
if (new_socket == INVALID_SOCKET) { return false; }
while (listenSocket) {
if(...){
char sendData[] = "hallo";
send(new_socket, sendData, sizeof(sendData), NULL);
}
When receiving the First Time everything is received as planned. However, the second time It only prints out Questions Marks in Squares "�". Is that because I'm sending a char array of 2048 chars that might only contain a lower amount than that or what could the problem be?
You are reading lines, i.e. character data terminated by \n, \r, or \r\n, as specified for BufferedReader.readLine(), but what you are sending isn't lines, it is null-terminated C strings. So you won't receive anything at all until the sender closes the connection, and then you will get a string containing who knows what where the nulls were.
You also aren't error-checking the result of send().
I think you should not send unnecessary data either way, so in your case only send the string with the following \0 or better and usually the default way the length of bytes and after that the data without \0 and only read a single string not a complete line.
Also be aware of the byte order (litte or big endian) when reading an integer as the data size for example.
I'm sure there is more documentation about how Java expects strings to be encoded for a socket connection or you have to read the raw bytes and convert it to a Java string yourself.
I have got a Java Server and a C# Client. And I'm really certain something goes wrong with the outputstreamer on the client or inputstream on the server.
byte[] arr = IOUtils.toByteArray(is,14);
PrintWriter out = new PrintWriter(os, true);
out.print("Received "+ new String(arr,"UTF-8"));
out.flush();
out.close();
"is" in this case is the Input Stream Variable. Coming from Socket.getInputStream().
Removing the length of the stream (14 in this case) makes the system time-out. The client does not get any respons except: "Read Failure"
The client side consists of a C# program
byte[] start = getIdentifier();
byte[] end = Encoding.UTF8.GetBytes(toSend);
byte[] arr = start.Concat(end).ToArray();
//Arr variable is a couple of strings smashed together and transformed into a byte[]
networkStream.Write(arr, 0, arr.Length);
networkStream.Flush();
StreamReader streamReader = new StreamReader(networkStream);
result = streamReader.ReadToEnd();
I actually tried writing to the Server with a BinaryWriter too, but that didn't work either. If I know what the length of the data is that will be send, I can fix the problem. But I do not know how to send the length of the stream to the server.
I've tried using Available() to see how much there was to read, but for some reason that sometimes was 0. As if the data wasn't been sent by the client yet.
Any ideas of how to fix the freezing?
EDIT:
I think I understand how the streaming works now.
At first I did this on the client side:
var length = BitConverter.GetBytes(arr.Length);
Array.Reverse(length);
Then on the server side I put this piece of code:
/* Get Input Data Length */
byte[] arr = IOUtils.toByteArray(is, 4);
int length = (arr[0] << 24) & 0xff000000 |
(arr[1] << 16) & 0x00ff0000 |
(arr[2] << 8) & 0x0000ff00 |
(arr[3] << 0) & 0x000000ff;
/* Get Input Data */
arr = IOUtils.toByteArray(is, length);
#Andreas your mention of the big-endian byte order made it clear how to send the length to the server
i have an implementation to do in a college project, and i dont know how to avchive it!. my problem its like the title say, conect android with c++ via sockets.
c++ must manage at least a pair of sockets clients (android Devices) or more.
so i try ...
#include <iostream>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>
using namespace std;
int main()
{
int client, server;
int portnum = 1500;
bool isExit = false;
int bufsize = 1024;
char buffer[bufsize];
struct sockaddr_in server_addr;
socklen_t size;
//init socekt
client = socket(AF_INET, SOCK_STREAM, 0);
if(client < 0){
cout << "Error Estableciendo la conexion" << endl;
}
cout <<"server Socket conexion creada" << endl;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(portnum);
//biding soket
if(bind(client,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0){
cout << "Error Biding Socket" << endl;
exit(1);
}
size= sizeof(server_addr);
cout << "buscando clientes" << endl;
//escuchando sokets
listen(client,1);
//accept client
server = accept(client, (struct sockaddr*)&server_addr, &size);
if(server < 0){
cout<< "Error al Aceptar" << endl;
exit(1);
}
while(server >0){
strcpy(buffer,"server conectado---\n");
send(server,buffer,bufsize,0);
cout <<"conectado con el cliente" << endl;
cout << "Ingresad # paara terminar la conexion" << endl;
cout <<"client: ";
do{
recv(server,buffer,bufsize,0);
cout << "buffer" << " ";
if(*buffer == '#'){
*buffer = '*';
isExit=true;
}
}while(*buffer != '*');
do{
cout << "\n server: ";
do{
cin >> buffer;
send(server,buffer,bufsize,0);
if(*buffer == '#'){
send(server,buffer,bufsize,0);
*buffer = '*';
isExit=true;
}
}while(*buffer != '*');
cout << "Client: ";
do{
recv(server,buffer,bufsize,0);
cout << buffer << " ";
if(*buffer == '#'){
*buffer = '*';
isExit = true;
}
}while(*buffer != '*');
}while(isExit);
cout << "Conection Terminated..." << endl;
cout << "Goodbye..." << endl;
isExit =false;
exit(1);
}
close(client);
return 0;
}
to listen every conection (please if you see bad logic in my code let me know it and what i need to do to fix it)
and in android i make something like this in a class that its called from an activity only for testing...
import java.net.Socket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Socktest {
public void prueba() {
try {
Socket sock = new Socket("127.0.0.1",1234);
// Your Java Code Verbatim:
BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream()));
String test = br.readLine();
System.out.println(test);
sock.close();
} catch (Exception ex) {
System.out.println(ex.getCause().toString());
}
}
}
(please if you see bad logic let me knowit because i want to learn more from my failures)
and the server goes fine when i compiled, its standing by to recive a new conection but when a luch the android activity and i run the "conection class" it says access denied, i am working on linux mint if that afect...
i am going to be gratfull with you coments, suggestions and advices.
Socket sock = new Socket("127.0.0.1",1234); this line is your problem right here, 127.0.0.1 is the local loop IP, its actually an IP address that maps to the device the code is on, so your android client is never connecting to the machine where the c++ code is running, instead what you need to do is find the IP address of the machine your c++ code is running on then change 127.0.0.1 to that IP address.
If you're mobile is connected to a wifi router to which your c++ server is connected to, then this IP address will be the local IP address, and everything is simple
If you're mobile is connected to some mobile network, and your server is on a different network, then you need to find out the IP address of your machine exposed to the world (depending on Internet service providers configuration you might have an IP address that remains static, or an IP address that changes every time your connection to the ISP server resets)
You can find your machines exposed IP address by going to websites like http://whatismyipaddress.com/ , then if your machine is behind a router, you need to do port forwarding (in this case forward your port 1234 to your machines local IP address) you can take a look at this guide to understand how to do that.
So to avoid the complexity in the 2nd step I advise you to connect your mobile and your server in same local network (i.e your LAN) using a wifi router.
Ways to find the IP address of your machine:
In linux based machines just open a terminal and type ifconfig and look at eth0 your IP address will be right after inet addr:, for example like this inet addr:10.99.123.23
I have a problem with sending directory names over socket from my C++ client, to my Java server.
Sending ordinary messages like "hello world", works great , but the following doesn't and I can not figure out what the problem is:
char const * files = ffd.cFileName; // get directory name
string str(files, 0, strlen(files)); // convert pointer to string, right?
char mess[str.size()];
strcpy(mess, str.c_str()); // make char array :)
cout << "Send file: " << mess << " with strlen: " << strlen(mess) << " and sizeof: " << sizeof(mess) << endl;
int sent = 0;
if ((sent = send(connectSocket, mess, sizeof(mess), 0)) == SOCKET_ERROR)
{
closesocket(connectSocket);
WSACleanup();
connectToServer();
}
The java server just receives the directory names like this:
wam
p
Win
dow
s
Win
dow
s.o
ld
wxW
idg
ets
I can not understand what I'm missing because I have tried every possible way to do this and the C++ client prints like:
"Send file: windows with strlen: 7 and sizeof: 7"
I do not think that the java server is the problem since I can receive normal strings and messages perfectly, but anyway here is the JAVA code:
is = socket.getInputStream();
byteArray = new byteArray[1024];
while (true) {
c = is.read(byteArray, 0, byteArray.length);
String recv = new String(byteArray, 0, c);
System.out.println(recv);
if (recv.equals("<EOF>")){
break;
}
list.add(recv);
}
If you request something else or anything just leave a comment and I will fix it.
Question: are you sending via TCP or UDP? I'm guessing TCP, and if that is the case, you need to treat the socket as more of a stream. That stream may get broken up into a bunch of packets - you don't really control that. What I might do is to prefix the string length of each directory (ex, 3foo, 4barz, etc), read from the socket and determine what constitutes as a logical block or string, and then assemble / print the strings based on that. If you go with that route, you need to track how much you read each time until you think you are done.
I solved it, Just added byteArray = new byte[1024]; and now it works:
while (true) {
byteArray = new byte[1024]; // I ADDED THIS AND NOW THE JAVA SERVER RECEIVES IT CORRECTLY!
c = is.read(byteArray, 0, byteArray.length);
recv = new String(byteArray, 0, c);
System.out.println(recv);
if (recv.equals("<EOF>")){
break;
}
list.add(recv);
}
I have a simple server written in Java, that just sends an Integer to a connected client. I have a client written in C, that connects to the server and prints out the received Integer.
My problem is that the result varies. About half of the times executing the client I get the correct result (234), but other times I get 8323072.
This is the server:
class TCPServer {
public static void main(String args[]) throws Exception {
ServerSocket welcomeSocket = new ServerSocket(6789);
while(true)
{
Socket connectionSocket = welcomeSocket.accept();
System.out.println("welcomeSocket.accept() called");
DataInputStream inFromClient = new DataInputStream(connectionSocket.getInputStream());
DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
outToClient.writeInt(234);
}
}
}
And this is the client:
int main(int argc, char *argv[])
{
if(argc != 4){
printUsage();
return;
}
char* serverIP = argv[1];
char* serverPortC = argv[2];
char* integerToSendC = argv[3];
int serverPort = atoi(serverPortC);
int integerToSend = atoi(integerToSendC);
int socketDesc = socket(AF_INET, SOCK_STREAM, 0);
if(socketDesc < 0) {
printf("Error when creating socket\n");
return;
}
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(serverPort);
inet_pton(AF_INET, serverIP, &serverAddr.sin_addr);
int connection = connect(socketDesc, (struct sockaddr*) &serverAddr, sizeof serverAddr);
if(connection < 0) {
printf("Error when establishing connection\n");
return;
}
char intBuffer[4];
if(recv(socketDesc, intBuffer, sizeof intBuffer, 0) == -1){
printf("Error while receiving Integer\n");
}
int receivedInt = ntohl(*((int *) &intBuffer));
printf("Received int: %d\n", receivedInt);
close(socketDesc);
}
Thanks in advance for any help!
Edit:
So in the end I did something like this, just for anybody who has the same problem:
while(receivedBytes < 4){
int readBytes = read(receiverSocket, &intBuffer, (sizeof intBuffer) - receivedBytes, receivedBytes);
receivedInteger += (intBuffer << (8*receivedBytes));
receivedBytes += readBytes;
}
Can you be sure you have received sizeof intBuffer bytes on the client side? No you can not, as recv() might return less bytes then requested.
Mod you code to loop around recv() as long as less bytes then requested have been received and no error occurred.
Note that recv()ing 0 bytes indicates the connection had been closed by the other side.
Also make sure the server side sends in network byte order.
Also^2: It is good idea to initialise variables (intBuffer here), at least during the development phase, will say: prior to the tuning phase.
Your problem could be because of the sub-boundaries of the various data types.
In Java, 4 bytes are assigned to int and 2 bytes for a short.
In C, 4 bytes are for long and 2 bytes for int.
This means Java int -> C long and Java short -> C int.
Now it depends on where your priorities lie.
If you want to perform an intense mathematical calculation in Java and send the result over socket to C, I recommend you do Java int -> C long or
If you want to just send small numbers and have the intense calculation done in C, do the Java short -> C int conversion.
Hope that helped.