www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Sockets between D and C(++) app

reply "Alexandre L." <alex.cs00 mail.com> writes:
Hello,
I lately did a minimal udp socket server-client application with 
C++, and I've been trying to recreate it with D, after.

I'm able to get the client's request (C++ client) without too 
much trouble (after I figured I needed to get it in ubyte[]).

Then I've tried to send the client an answer through send() (then 
tried sendTo() ) but none of them work.

Here's my 'server' code:

int main(string[] args)
{
	auto s = new UdpSocket();

	auto addr = new InternetAddress("127.0.0.1", 6666);
	s.setOption(SocketOptionLevel.IP, SocketOption.REUSEADDR, true);
	s.bind(addr);

	while (true)
	{
		ubyte[2048] recv_buf;
		int count = s.receiveFrom(recv_buf);
		char[] test = cast(char[])recv_buf[0..count-1]; // -1 for C 
string comp.

		writefln("Received: %s\n", test);

		char[] rep = "regan\0".dup;
		
		s.send(cast(ubyte[])rep);
	}

	writeln("End!");

	return 0;
}

Then the client tries to get the string in a std::string, hence 
why I did input the \0 literal. I'm also fairly new to D, so any 
guidance is sure welcomed!

Alexandre
Apr 01 2014
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Alexandre L.:

Some comments on your code:

 Here's my 'server' code:

 int main(string[] args)
 {
If you don't need args, then I suggest to not put it as main argument. So probably this is better (note no int nor return, in D they are not needed): void main() { ... }
 		int count = s.receiveFrom(recv_buf);
It's better to use immutable here, and infer the type (use const/immutable everywhere you don't really need to mutate a variable/argument/field): immutable count = s.receiveFrom(recv_buf);
 		char[] test = cast(char[])recv_buf[0..count-1]; // -1 for C 
 string comp.

 		writefln("Received: %s\n", test);

 		char[] rep = "regan\0".dup;
 		
 		s.send(cast(ubyte[])rep);
casts are dangerous, because they silently assume you know what you are doing. As first try I suggest you to remove every cast() from your D program, and replace them with to!() or other functions like toStringz. Sometimes this is not the most efficient thing to do, but it's safer, so it's better when you start to learn D. Bye, bearophile
Apr 01 2014
next sibling parent reply "Alexandre L." <alex.cs00 mail.com> writes:
On Wednesday, 2 April 2014 at 00:34:08 UTC, bearophile wrote:
 		char[] rep = "regan\0".dup;
 		
 		s.send(cast(ubyte[])rep);
casts are dangerous, because they silently assume you know what you are doing. As first try I suggest you to remove every cast() from your D program, and replace them with to!() or other functions like toStringz. Sometimes this is not the most efficient thing to do, but it's safer, so it's better when you start to learn D. Bye, bearophile
Thanks for your reply. As for cast, I seems not to have any option, because to! doesn't work as I would expect it. It would return me an array of numbers, instead of a string; So I kept the cast here, since I certainly know what it's doing -for now-. I generally use immutables, you caught me, here :-). As of main and return, I was not aware we could just ignore them if we didn't need them. I love explicit programming. However, I'm still stuck with toStringz(). Since it returns an immutable(char[]), I can't find how to pass it to Socket.send(), and I do not seem to be able to cast away the immutable :-s Alexandre
Apr 01 2014
parent "Alexandre L." <alex.cs00 mail.com> writes:
My bad; It returns immutable(char)*.

Still won't work with send(); Am I right to supposed the 
receiving client must handle a ubyte[] as well (C++) ?
Apr 01 2014
prev sibling parent reply Russel Winder <russel winder.org.uk> writes:
On Wed, 2014-04-02 at 00:34 +0000, bearophile wrote:
 Alexandre L.:
 int main(string[] args)
 {
If you don't need args, then I suggest to not put it as main argument. So probably this is better (note no int nor return, in D they are not needed): void main() { ... }
I am not convinced by this argument. The return value (aka exit code) is always present, if you ignore this by having a void main, it will return 0, i.e. main is not a void function even if you claim it is. As for ignoring the parameters, this is making use of the fact that main is the only function where you do not have to present the correct signature. Perhaps this exception should be removed. The real signature of main in C/C++ is, I believe: int main(int, char**, char**) what is the real signature in D? -- Russel. ============================================================================= Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.net 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Apr 02 2014
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Russel Winder:

 what is the real signature in D?
The _real_ signature of main() is flexible, you can use: void main() int main(in string[] args) pure nothrow D allows you to omit the args if you don't need them, returns 0 if you don't it, and it can be pure/nothrow/ safe as desired.
 Perhaps this exception should be removed.
This special cases cause zero bugs and zero problems, so there is no chance to change this, even if we want (and I don't want). Bye, bearophile
Apr 02 2014
prev sibling next sibling parent reply "Dicebot" <public dicebot.lv> writes:
On Wednesday, 2 April 2014 at 08:55:23 UTC, Russel Winder wrote:
 On Wed, 2014-04-02 at 00:34 +0000, bearophile wrote:
 Alexandre L.:
 int main(string[] args)
 {
If you don't need args, then I suggest to not put it as main argument. So probably this is better (note no int nor return, in D they are not needed): void main() { ... }
I am not convinced by this argument. The return value (aka exit code) is always present, if you ignore this by having a void main, it will return 0, i.e. main is not a void function even if you claim it is. As for ignoring the parameters, this is making use of the fact that main is the only function where you do not have to present the correct signature. Perhaps this exception should be removed. The real signature of main in C/C++ is, I believe: int main(int, char**, char**) what is the real signature in D?
D main != C main, latter is implemented in D runtime to call the former. 0 will be also returned by latter, not the former. Also exception will result in >0 status code even if return type is void. It effectively just says that you won't manage status code manually and allows runtime to take care of it.
Apr 02 2014
parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 4/2/14, Dicebot <public dicebot.lv> wrote:
 D main != C main, latter is implemented in D runtime to call the
 former. 0 will be also returned by latter, not the former.
Actually, the compiler injects a return statement in D's main. It generates the actual C main function (unless WinMain/DllMain is provided), which calls another special D runtime init function, which itself calls the D main function (which itself has the injected return statement unless it's already an int return). It's quite complicated, but it's all open-source so you can inspect it.
Apr 02 2014
prev sibling parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Wednesday, 2 April 2014 at 08:55:23 UTC, Russel Winder wrote:
 The real signature of main in C/C++ is, I believe:

         int main(int, char**, char**)
I believe in C it is: int main(void) int main(int,char**) or implementation defined (e.g. the third env pointer in the main signature from unix) But the actual entry point is system dependent and can be specified as a link option? On linux "_start" or something like that.
Apr 02 2014
prev sibling parent reply "FreeSlave" <freeslave93 gmail.com> writes:
It's only server. Maybe problem is on client side.

Try this if you are on Linux:

//Linux C client
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>

int main()
{
     int sock, res;
     struct sockaddr_in addr;
     const char* hello;
     size_t len;

     sock = socket(AF_INET, SOCK_DGRAM, 0);
     if (sock < 0)
     {
         printf("Can't create socket\n");
         return -1;
     }

     addr.sin_family = AF_INET;
     addr.sin_port = htons(5432);
     addr.sin_addr.s_addr = inet_addr("127.0.0.1");

     hello = "Hello from C";
     len = strlen(hello);
     while (1)
     {
         res = sendto(sock, hello, len, 0, (struct 
sockaddr*)&addr, sizeof(addr));
         if (res <= 0)
         {
             printf("Can't send\n");
             return -1;
         }
     }
     close(sock);
     return 0;
}


//D server
import std.socket;
import std.stdio;

int main(string[] args)
{
     auto s = new UdpSocket(AddressFamily.INET);

     auto addr = new InternetAddress("127.0.0.1", 5432);
     s.setOption(SocketOptionLevel.IP, SocketOption.REUSEADDR, 
true);
     s.bind(addr);

     while (true)
     {
         ubyte[2048] recv_buf;
         int count = s.receiveFrom(recv_buf[]);
         char[] test = cast(char[])recv_buf;

         writefln("Received: %s\n", test);
     }
     return 0;
}


Note that you don't need to remove zero-symbol if you don't pass 
it from client.
Apr 02 2014
parent "Alexandre L." <alex.cs00 yahoo.ca> writes:
On Wednesday, 2 April 2014 at 21:54:58 UTC, FreeSlave wrote:
 It's only server. Maybe problem is on client side.
Yes, it is only a server which needs to answer back the client; And there was the problem: I was not fetching the client's address, and since UDP is an unconnected protocol, I couldn't send it back with just "send". So what I had to do was to create a client address and pass it in receiveFrom(), and then use sendTo(in data, in clientAddr). Once this was fixed, the server was able to answer my client. N.b. auto addr_client = new InternetAddress didn't seem to work, like if InternetAddress wasn't right for the Address parameter. I explicitely needed Address. Here's the code: module main; import std.stdio; import std.socket; import std.string; import std.conv; int main() { auto s = new UdpSocket(); auto addr = new InternetAddress("127.0.0.1", 6666); s.setOption(SocketOptionLevel.IP, SocketOption.REUSEADDR, true); s.bind(addr); Address addr_client = new InternetAddress(6666); while (true) { ubyte[2048] recv_buf; immutable count = s.receiveFrom(recv_buf, addr_client); char[] test = cast(char[])(recv_buf[0..count-1]); // -1 pour compatibilité avec C string... writefln("Received: %s\n", test); auto rep = toStringz("Hello"); s.sendTo(rep[0..6], addr_client); } return 0; } Thanks all for your help, much appreciated! Alexandre
Apr 03 2014