Sign in to follow this  
yahn

cross platform-ness

Recommended Posts

I wrote a couple socket libraries for a program called Game Maker. I don't know if anyone knows of it, but that really doesn't matter. The problem is: I know how to write a library for windows, but I doubt it will work on *nix and mac systems. If I write the code using winsock2 it probably wont work on *nix and mac, right? So, where exactly do I get the files Beej talks about in his guide. I want to be able to compile something on windows that will work on windows, *nix and mac. Is that even possible? Sorry if this question has already been answered a lot. I've been searching around for it and havn't found anything direct. Thank you.

Share this post


Link to post
Share on other sites
Game Maker only works on Windows.

Also,
Quote:
Original post by yahn
I want to be able to compile something on windows that will work on windows, *nix and mac. Is that even possible?

No, it's not. You have to compile it separately on each system (or have the end user do it, which is what a lot of open source libraries do).

Share this post


Link to post
Share on other sites
The Winsock API has the same commands as the Berkeley Sockets API except for the functions that begin with WSA.

You can make it somewhat cross-platform by avoiding the WSA functions, but the libray would have to be recompiled(if this is possible with Game Maker) on the target system for them to use it.

Edit: Porting sockets to Winsock. It does the opposite of what you want, but it does help show the differences in the APIs.

Share this post


Link to post
Share on other sites
As far as I know, C++ cross-compilation between Windows and Linux (and other platforms) is at least theoretically possible using MinGW. You still can't get one executable to run on all platforms (short of using an emulator), but it should be possible to at least compile both a native version and a cross version.

Share this post


Link to post
Share on other sites
Windows and Linux tend to be easy. Common winsock calls are either identical, or can be mapped through simple macros.

The real problem with cross-platform is the endianess. Unless you're already taking care of that for everything you read or write through sockets, then your current code isn't portable.

There's plenty of socket wrappers, depending on your choice of language. Just keep in mind that there'll be many very subtle issues which will make development a really slow process.

Another issue is the general portability of your code.

Compiling one library on windows is possible, as long as you find a cross-compiler. For Linux it should be pretty somewhat doable with gcc. But your best bet would probably be VMWare.

All in all, not a trivial task, since you'll be doing everything times 5. And then there's various platform specific version issues and incompatibilities....

Share this post


Link to post
Share on other sites
I'm not using GM anymore. The reason I am no longer using it is I want to be cross platform.

Why can't I use Berkeley Sockets in Windows? Or can I? Also, does Mac use Berkeley Sockets or it's own thing?

What about just using HawkNL? I didn't think this would be very much work, but from the sounds of it I'm thinking that linking to a library would be a much better option.

Does anyone know anything good or bad about HawkNL? I know that RakNet uses it. So, I'm guessing it does the job.

I don't want to use RakNet because it isn't free. And I'm planning on this being free when I'm finished.

Thank you.

Share this post


Link to post
Share on other sites
You can use a portable library, such as SDL_Net, HawlNL, Plib or Enet. Or you can write to the plain Berkeley Sockets API, with small variations for each platform (on Winsock, you need WSAStartup(), for example). The small variations can be hidden away in a header file of your own choosing.

Share this post


Link to post
Share on other sites
If I use HawkNL do I have to worry about endianness or stuff like WSAstartup()? I would imagine that HawkNL would do all of that for me. If so then I think I'll just use that instead of reinventing the wheel.

Share this post


Link to post
Share on other sites
I don't know about HawkNL specifically, but the contents of the data you send is not typically handled by the networking abstraction. The content of what you put in your packets is something you're always responsible for, and if you want to send a binary integer from one platform to another, you need to somehow make sure that they agree on the representation of that packet.

Typically, that will be done through a serialization or marshaling API. For example, Etwork has such an API, although Etwork hasn't been widely ported beyond WinSock. RakNet, and most of the other "higher level" libraries mentioned in the Forum FAQ also do the same thing.

Share this post


Link to post
Share on other sites
The only problem I have is a lot of these libraries do too much. I'm not exactly sure how I would deal with endianess, so it would be nice if there was just a library that would wrap the socket functions on all platforms and deal with endianess.

Exactly how hard would it be to deal with endianess myself? Couldn't I just use one byte for weather it is big or little endian at the beginning of every packet and switch it around if it is the opposite on the other end?

That doesn't sound all that difficult, but I'm not really sure. I guess it could get a little complicated when you are packing bits into bytes.

Anyways, can anyone tell me if I've got anything wrong with the my endian plan? And does anyone have a reason why I shouldn't use HawkNL? Does anyone even know anything about HawkNL?

Share this post


Link to post
Share on other sites
Quote:
it would be nice if there was just a library that would wrap the socket functions on all platforms and deal with endianess


The problem is that you need to know what the data is to be able to deal with endian-ness. Only your application knows that. Most of the libraries that go to the length of letting you describe your application data, by necessity, do a lot of other things too.

The most common solution is to build a serializer class, and use operator overloading or get/set functions to transfer data. Those functions would, in turn, do the appropriate swapping.

Rather than having a byte for format at the front, just define a format for network packets. For example, say that all packets are always in network byte order (big endian), and make the serializer class convert as necessary when running on a little-endian machine.

The best way to structure that code is to define a visitor template for each struct you want to send, that calls a template argument for each member. Then you can pass in an actor that does serialization, or de-serialization, and use the same visitor function, which reduces the risk of the reading and writing parts falling out of sync.


struct A {
int value;
float x, y, z;

template<typename Actor>
static void visit(A &a, Actor &act) {
act.visit(value, 0, 100000); // min/max values
act.visit(x, 0.01, -5000.0, 5000.0); // precision, min/max values
act.visit(y, 0.01, -5000.0, 5000.0); // precision, min/max values
act.visit(z, 0.01, -5000.0, 5000.0); // precision, min/max values
}
};

struct B {
std::string name;
int hitpoints;

template<typename Actor>
static void visit(B &b, Actor &act) {
act.visit(name);
act.visit(hitpoints, 0, 255); // min/max values
}
};

// the generic visitor function
template<typename T, typename Actor> void Visit(T &t, Actor &a) {
T::visit(t, a);
}

class Writer {
public:
Writer();
void visit(int i, int min, int max) {
int bytes = htonl(i);
write_bytes(&bytes, sizeof(bytes)); // ignore min/max
}
void visit(float f, float prec, float min, float max) {
int bytes = htonl((int)(f / prec));
write_bytes(&bytes, sizeof(bytes)); // ignore min/max
}
void visit(std::string &str) {
int bytes = htonl(str.length());
write_bytes(&bytes, sizeof(bytes));
write_bytes(str.c_str(), str.length());
}
};

int SendMessage() {
// let's assume this sends a message containing an A and a B
A a;
B b;
b.name = "my name";
a.value = 7;
a.x = 14.5;
...
Writer wr;
Visit(a, wr);
Visit(b, wr);
return ::sendto(socket, wr.data(), wr.size(), 0, &addr, sizeof(addr));
}



Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this