|
|
|
|
|
|
| |
| |
|
|
|
|
| |
| |
|
|
OK, so I've got a small but fairly complicated client/server program,
using a trivial custom communications protocol that I wrote myself.
Basically I want the client program to remotely execute methods in the
server program. And, as you might suspect, it turns out there are 3rd
part libraries for doing that kind of thing automatically.
One such library is Microsoft's "WCF". (No, I have no idea what that
stands for.) The idea, according to what little documentation I can
find, is that you write a C# interface, write some code that implements
that interface, and then you can hook up a client so it can call the
interface methods just as if they were local.
Trouble is, WCF is a huge, sprawling framework with a bazillion bells
and whistles. It's almost impossible to make any sense out of the API
documentation. (For one thing, it's mostly extremely terse.) What's
really lacking is something to explain the high-level abstractions and
give you somewhere to *start*...
Oh, there *is* documentation, mind you. Both from Microsoft themselves
and on various blogs and so on. But they all say something along the
lines of "open Visual Studio, select the WCF Class Library template,
watch it generate eleventy-billion lines of XML configuration file which
isn't explained or documented anywhere on the face of the Internet".
Most horrifyingly of all, it seems that absolutely everything to do with
WCF defaults to turning method calls into XML data [which seems
reasonable enough] and then transmitting it via HTTP [which is the most
absurd thing I've ever heard of]. Almost nowhere does it tell you how to
turn off the completely unnecessary HTTP layer and just run on plain TCP
sockets.
(Seriously. HTTP provides *nothing* which is useful for transferring
transient data. It's just pointless overhead. What the hell is *wrong*
with the world? *Why* must *everything* be kludged to work over HTTP
these days? Sheesh...)
If you walk through the examples, it does eventually work. But the way
it works is bizarre: You've written a C# interface, which lists exactly
what methods your server exposes. And yet, what they want you to do is
enable "metadata exchange (mex)", and then run a command-line tool which
connects to the server, downloads the interface description, and
generates C# code for talking to the server.
Even though the necessary information is ALREADY sitting RIGHT THERE in
that interface you just wrote! WTF?
(I suppose if the server was written by somebody on the other side of
the planet and you don't have the source code, being able to dynamically
look up its API is nice. But for any other use-case... why this much
complexity??)
Sadly, it seems that mex cannot ever be made to operate over TCP, only
HTTP. (Christ knows why...) Also disturbing is that if you use the magic
WCF template, Visual Studio somehow "knows" if your service is running
or not, and automatically starts and stops it when you try to run
clients that use it. This makes me very, very nervous!
Well, you know what? All that stuff I just said? You can completely
ignore that bunch of nonsense. Because it turns out that all you
*actually* need to do is this:
1. Write a C# interface, with the right attributes on it.
2. Write a class in your server that implements this interface.
3. Execute THREE LINES OF CODE in your server's Main() function, telling
it what class implements the interface, and where to listen for clients.
4. Write THREE LINES OF CODE in the client, using the interface type as
a generic parameter. You get passed back a proxy object which implements
the interface - but whenever you call a method, it actually executes on
the server. Transparently. With no effort from you other than writing
THREE LINES OF CODE.
So, yeah. The client and the server both reference the same C# interface
type, and WCF automagically transports arguments and results between
client and server. Other than figuring out how to connect to each other,
there's almost no code involved. In particular, you DO NOT need a
200-line XML file with undocumented contents to make this work!
Now, if something somewhere on the Internet would have just *said* all
that, I could have saved myself many, many hours of head-banging...
Of course, this all means much less code needs to be written. But on the
other hand, I just surrendered control to an external library, so now
it's much harder to work out why things aren't working. ;-)
It seems that, by default, opening a connection creates your proxy
object, but doesn't actually *connect* to the server until you try to
call a method. Which means that if anything goes wrong, instead of
throwing an exception in the connection method, it throws an exception
in some other random place in your application, depending on where you
just happen to try to use the server for the first time. :-S
It's easy enough to fix that one. It's not documented, but there's an
Open() method which forces the connection to be opened *right now*. You
know, so I can decide whether to proceed or not based on whether the
server is *actually* listening to me or not.
Unfortunately, it seems by default, if the server is *not* listening,
the Open() method simply waits forever. This is not very helpful.
Fortunately, you can trivially specify a timeout as a parameter to the
method. So that's OK.
Now the *real* fun begins! You see, I actually want to use this stuff on
a mixture of .NET and Mono. I wrote a small test project to check that I
could get this stuff to work, but I only tried it on .NET, and only on
the local machine. When I tried to modify the *real* application to use
WCF, suddenly I ran into several problems.
The first problem is that Mono *refuses* to listen on all network
interfaces. Won't do it. You can easily to it with .NET, but Mono won't.
https://bugzilla.xamarin.com/show_bug.cgi?id=275
To sumarise: If you ask .NET to listen on "localhost", it will accept
any and all incoming network connections. If you ask Mono to listen to
"localhost", it will *only* accept connections from the local machine.
Under .NET, you can listen on "0.0.0.0" to allow connections from all
configured IP addresses (including loopback). But on Mono, this THROWS
AN EXCEPTION. (!!) Finally, you can listen on one of your machine's
configured IP addresses (assuming you can FIND one...), in which case
only connections to that address are accepted.
To sumarise the summary: Mono can listen to internal connections OR
external connections, but not both at the same time [which is the
*default* action under .NET] Oh, well, unless you explicitly configure
TWO listening addresses...
So it seems an easy hack for that is to listen on whatever
Dns.GetHostName() returns. Now remote connections are allowed. (But not
local ones under Mono. But hey, the real application happens to not need
local connections...)
Next, a rather more strange issue. It seems that if you run my client
and server under .NET, everything is fine. If you run them both under
Mono, everything is fine. But if you run one under .NET and one under
Mono... the connection just hangs forever. (Or times out if you
specified a timeout value.) What the...?
OK, so that this point I'm guessing defaulting shenanigans. And it turns
out I'm right:
https://bugzilla.xamarin.com/show_bug.cgi?id=276
By default, .NET runs TCP with SSL turned on, while Mono defaults to
having SSL turned off. So presumably the client (.NET) is sitting there
waiting for some SSL handshake data, while the server (Mono) has no
intention of sending any such data - and has no idea the client is
waiting for it.
What, no feature negotiation step? :-P
The solution is pretty simple; just explicitly specify whether SSL
should be on or off. (And specify it the same way for both client and
server.) But man, do you have *any idea* how many protocol configuration
options there are?? It's a damned good thing this is the only one who's
default value differs between .NET and Mono, because otherwise I'd have
to explicitly configure every single one of them...!
Hey, wait a sec. Bug #275 is a WCF bug in Mono, and bug #276 is
*another* WCF bug in Mono. Do you suppose #277 is...
https://bugzilla.xamarin.com/show_bug.cgi?id=277
...yes, of course it is. It's *another* WCF bug in Mono. Oh goodie!
Essentially, .NET allows multiple services to listen on the same TCP
port, provided they each have a different URL. (Presumably one component
listens, then when it sees the URL it routes the connection to the
appropriate handler or something...) If you attempt to do this under
Mono, it simply throws an exception at you. Let me say that again: A
perfectly acceptable, working program on .NET summarily throws an
exception if you run it under Mono.
Oh dear. I was actually contemplating *using* that particular feature...
oh well, I guess not! (Makes that part of the URL _completely_ pointless
now though, doesn't it?)
So I deploy my new WCF code, and I hit upon *another* problem: It seems
that after a while, the server disconnects for no apparent reason.
Searching the Internet gives me nothing, until *eventually* I stumble
upon a vague reference to ReceiveTimeout.
Reading the scant documentation, it appears that if the server doesn't
hear anything for this number of seconds, it disconnects the client. The
intention seems to be that a client can't hog server resources by just
connecting and then saying nothing.
OK, a couple of things.
1. I can see how this might be bad if you were offering this service to
the entire world (or maybe just a large internal network). However, I'm
using this for 1 server with 1 client. This is an internal test
framework. It will never be exposed to the outside world. I don't *care*
about resource usage!
2. Doesn't TCP *already* send periodic heartbeat messages and close the
connection if it doesn't get a reply for a while? Why is WCF trying to
duplicate this functionality when it's already present??
Well anyway, all you have to do is go set ReceiveTimeout to some higher
value. (The documentation is inconsistent, but I believe this defaults
to 10 minutes - which is too soon, considering some of my tests take
half an hour.)
Oh, wait a sec... That works on .NET, but under Mono is has ABSOLUTELY
NO EFFECT. Wuh?! O_O
OK, so under Mono it appears to be _impossible_ to stop the server
disconnecting. So there's that. Also, while in the process of doing
this, I found that on .NET you can say AddDefaultEndpoints(), and it
does what the documentation says. However, when you run this under Mono,
it THROWS AN EXCEPTION saying the required method DOES NOT EXIST! :-O
Fortunately, it turns out I don't really need this method. It's trivial
enough to generate the default endpoint by hand. But sheesh... the
entire experience of WCF on Mono just seems to be a steaming pile of
broken. And I really don't know how to fix this disconnect issue; it's
looking like I'll have to write a pile of code to manually send
heartbeat messages to the server just to hold the connection open. (You
know - the EXACT SAME THING that the TCP layer is ALREADY DOING!!)
When you have to write MORE CODE to disable a supposed "feature" of the
framework you're using... yeah, what can you say?
On the plus side, if I ever get this stuff to work, it'll be beautiful.
I especially like the way I can make it so if the server throws an
exception, the client gets the exception (rather than just hang forever
or spit out a socket exception). I suspect this is going to make things
far more reliable...
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 06/11/2013 08:43 PM, Orchid Win7 v1 wrote:
> Hey, wait a sec. Bug #275 is a WCF bug in Mono, and bug #276 is
> *another* WCF bug in Mono. Do you suppose #277 is...
>
> https://bugzilla.xamarin.com/show_bug.cgi?id=277
>
> ...yes, of course it is. It's *another* WCF bug in Mono. Oh goodie!
OK, so admit it: Now you want to know if #278 is WCF as well! :-D
Well no, it isn't. But I just this second realised that #274 is WCF. (It
seems a global constant is set to null under Mono.)
#273 isn't WCF though.
What I find amusing is that these bugs were reported in 2011, and most
of them are still open...
(And by "amusing" I of course mean some suitable antonym of "amusing"...)
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 06/11/13 20:43, Orchid Win7 v1 wrote:
> OK, so I've got a small but fairly complicated client/server program,
> using a trivial custom communications protocol that I wrote myself.
<snip>
> On the plus side, if I ever get this stuff to work, it'll be beautiful.
> I especially like the way I can make it so if the server throws an
> exception, the client gets the exception (rather than just hang forever
> or spit out a socket exception). I suspect this is going to make things
> far more reliable...
Thankyou Andrew for assuring me that the world still goes on (see
preceding post)
John
--
Protect the Earth
It was not given to you by your parents
You hold it in trust for your children
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 06/11/2013 08:47 PM, Doctor John wrote:
> Thankyou Andrew for assuring me that the world still goes on
There's no charge for this service. :-)
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
> 3. Execute THREE LINES OF CODE in your server's Main() function, telling
> it what class implements the interface, and where to listen for clients.
>
> 4. Write THREE LINES OF CODE in the client, using the interface type as
> a generic parameter.
Aren't you going to share those three lines of code?
> Now the *real* fun begins! You see, I actually want to use this stuff on
> a mixture of .NET and Mono.
It wasn't too long ago you were complaining how you didn't even have a
.net runtime installed and it was a huge bloated monster and didn't
understand why anyone would want to use it - and you would have told us
all that it was obviously impossible to get any .net code working on
anything other than Windows. What a change (for the better)! :-D
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 07/11/2013 08:21 AM, scott wrote:
>> 3. Execute THREE LINES OF CODE in your server's Main() function, telling
>> it what class implements the interface, and where to listen for clients.
>>
>> 4. Write THREE LINES OF CODE in the client, using the interface type as
>> a generic parameter.
>
> Aren't you going to share those three lines of code?
OK, so here's what you have to do.
First, write your interface:
[ServiceContract]
public interface IDoStuff
{
[OperatioContract]
double DoTheStuff(double x, double y);
}
Next, write the server code that actually does it.
public class RealDoStuff : IDoStuff
{
...
}
Now, to serve the interface to the outside world, do this:
var host = new ServiceHost(typeof(RealDoStuff));
host.AddEndpoint(typeof(IDoStuff), "net.tcp://localhost:1664/DoStuff/");
host.Open();
Clients can now connect and run any method mentioned in IDoStuff.
Speaking of which... On the client end, do this:
var binding = new NetTcpBinding();
var factory = new ChannelFactory<IDoStuff>(binding,
"net.tcp://localhost:1664/DoStuff/");
var proxy = factory.CreateChannel();
The "proxy" object implements IDoStuff. You can call it like any other
local object. But actually, when you call it, it calls your server.
Actually, the client doesn't attempt to make a network connection until
the first time you call a method on the proxy - which is a bit annoying!
You can force it to open immediately, however:
((ICommunicationObject)proxy).Open();
A similar technique can be used to explicitly close the connection if
you want to.
Compare this to, for example,
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<!-- specifies the version of WCF to use-->
<supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.5,Profile=Client" />
</startup>
<system.serviceModel>
<bindings>
<!-- Uses wsHttpBinding-->
<wsHttpBinding>
<binding name="WSHttpBinding_ICalculator" />
</wsHttpBinding>
</bindings>
<client>
<!-- specifies the endpoint to use when calling the service -->
<endpoint
address="http://localhost:8000/ServiceModelSamples/Service/CalculatorService"
binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_ICalculator"
contract="ServiceReference1.ICalculator"
name="WSHttpBinding_ICalculator">
<identity>
<userPrincipalName
value="mig### [at] redmondcorpmicrosoftcom" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration><?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.5,Profile=Client" />
</startup>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ICalculator" />
</wsHttpBinding>
</bindings>
<client>
<endpoint
address="http://localhost:8000/ServiceModelSamples/Service/CalculatorService"
binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_ICalculator"
contract="ServiceReference1.ICalculator"
name="WSHttpBinding_ICalculator">
<identity>
<userPrincipalName
value="mig### [at] redmondcorpmicrosoftcom" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
*This* is the way Microsoft insists you have to do it.
To be fair, this has the advantage that you can change the network
configuration without recompiling your client or server code. But in my
case, since this is an internal application which only I will ever use,
this "advantage" is moot.
>> Now the *real* fun begins! You see, I actually want to use this stuff on
>> a mixture of .NET and Mono.
>
> It wasn't too long ago you were complaining how you didn't even have a
> .net runtime installed and it was a huge bloated monster and didn't
> understand why anyone would want to use it - and you would have told us
> all that it was obviously impossible to get any .net code working on
> anything other than Windows. What a change (for the better)! :-D
Well, when somebody pays you to write .NET code, you spend an hour or
two installing .NET (Actually, VS takes multiple hours to install...) It
wasn't my decision to use C#. ;-)
Having said that, C# is basically like Java, but less broken. It even
has real generics (rather than the fake generics they kludged into
Java). Hell, you can write *monads* in C#! ;-)
I'm still pretty sure that .NET was invented for the sole purpose of
vendor lock-in though. And back when I was whining about it, Mono didn't
exist. :-P
Man, the amount of bugs in the Mono WCF stuff... >_<
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
> OK, so here's what you have to do.
...
That seems pretty simple, I'll file that in "useful stuff to remember" :-)
Someone at work introduced me to Arduino a few months ago (they used one
to control a test rig here), I was so impressed how simple it was to set
up and code for that I ordered one myself to tinker with at home - so
that's taking up my "spare" time at the moment. Along with decorating
the house, and playing iRacing, and keeping fit, and ... Fiddling with
C# is pretty low down the list nowadays.
> I'm still pretty sure that .NET was invented for the sole purpose of
> vendor lock-in though. And back when I was whining about it, Mono didn't
> exist. :-P
I'm sad that MS have discontinued proper 3D graphics support with C#.
You used to have the option between XNA or managed DirectX, both of
which were pretty easy to use at perfectly good enough performance
levels. Now you have to use C++ and Windows 8, which is a shame as
that's not really justification enough for me to upgrade from Windows 7
(not to mention relearning all the differences between C++ and C#).
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
>
> Most horrifyingly of all, it seems that absolutely everything to do with
> WCF defaults to turning method calls into XML data [which seems
> reasonable enough] and then transmitting it via HTTP [which is the most
> absurd thing I've ever heard of]. Almost nowhere does it tell you how to
> turn off the completely unnecessary HTTP layer and just run on plain TCP
> sockets.
>
> (Seriously. HTTP provides *nothing* which is useful for transferring
> transient data. It's just pointless overhead. What the hell is *wrong*
> with the world? *Why* must *everything* be kludged to work over HTTP
> these days? Sheesh...)
>
There are two reasons for this:
- Everybody and their grandmother knows how to handle HTTP. You don't
need to rewrite a custom interface to handle AndrewTP over port 31337
when you want to interface with the application. (Just dealing with the
XML parser is enough headaches already!)
- Firewalls
> So I deploy my new WCF code, and I hit upon *another* problem: It seems
> that after a while, the server disconnects for no apparent reason.
> Searching the Internet gives me nothing, until *eventually* I stumble
> upon a vague reference to ReceiveTimeout.
>
> Reading the scant documentation, it appears that if the server doesn't
> hear anything for this number of seconds, it disconnects the client. The
> intention seems to be that a client can't hog server resources by just
> connecting and then saying nothing.
>
> OK, a couple of things.
>
> 1. I can see how this might be bad if you were offering this service to
> the entire world (or maybe just a large internal network). However, I'm
> using this for 1 server with 1 client. This is an internal test
> framework. It will never be exposed to the outside world. I don't *care*
> about resource usage!
Then program a keepalive message at the application level.
>
> 2. Doesn't TCP *already* send periodic heartbeat messages and close the
> connection if it doesn't get a reply for a while? Why is WCF trying to
> duplicate this functionality when it's already present??
>
Not always.
TCP keepalive is an option and defaults to off.
Secondly, you may have many short-lived TCP connections part of the same
long-lived application "session" (e.g.: online shopping, or http 1.0)
> Well anyway, all you have to do is go set ReceiveTimeout to some higher
> value. (The documentation is inconsistent, but I believe this defaults
> to 10 minutes - which is too soon, considering some of my tests take
> half an hour.)
>
How does your client know the difference between the server processing
the request and the server having exploded in a firey ball of ones and
zeroes?
When you get put on hold, do you prefer hearing a "your call is
important to us, please remain on the line to retain your calling
priority" reminder ever minute or so, or just hearing dead air, not
knowing if the agent hung up on you, or it looking at your file?
Client/server apps are the same. If you want the communication channel
to remain open, you should program a keepalive, and ideally an
erro-recovery mechanism where it will try to reconnect in case the
keepalive gets eaten by goblins.
> Oh, wait a sec... That works on .NET, but under Mono is has ABSOLUTELY
> NO EFFECT. Wuh?! O_O
>
> OK, so under Mono it appears to be _impossible_ to stop the server
> disconnecting.
At the risk of repeating myself... program an application-level keepalive.
--
/*Francois Labreque*/#local a=x+y;#local b=x+a;#local c=a+b;#macro P(F//
/* flabreque */L)polygon{5,F,F+z,L+z,L,F pigment{rgb 9}}#end union
/* @ */{P(0,a)P(a,b)P(b,c)P(2*a,2*b)P(2*b,b+c)P(b+c,<2,3>)
/* gmail.com */}camera{orthographic location<6,1.25,-6>look_at a }
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
>> (Seriously. HTTP provides *nothing* which is useful for transferring
>> transient data. It's just pointless overhead. What the hell is *wrong*
>> with the world? *Why* must *everything* be kludged to work over HTTP
>> these days? Sheesh...)
>>
>
> There are two reasons for this:
> - Everybody and their grandmother knows how to handle HTTP. You don't
> need to rewrite a custom interface to handle AndrewTP over port 31337
> when you want to interface with the application. (Just dealing with the
> XML parser is enough headaches already!)
But this is *still* using XML - it's just that it's sending it on top of
HTTP rather than over a raw TCP socket.
> - Firewalls
This is not a valid reason.
> Then program a keepalive message at the application level.
Right, so now I have to have a second thread sitting in the background
with a timer, and I have to remember to reset that timer every time a
command is sent, and then when the timer runs down, I send a null
command and reset the timer... Surely all this boilerplate code should
be in the framework?
Seriously, when I used plain TCP, I didn't need to do this. The helpful
framework is forcing me to do *more* work!
(Well, technically it's not the framework's fault that Mono implements
it wrong, but anyway...)
> How does your client know the difference between the server processing
> the request and the server having exploded in a firey ball of ones and
> zeroes?
No, this is the *server* not waiting for the *client*.
> Client/server apps are the same. If you want the communication channel
> to remain open, you should program a keepalive
This shouldn't be necessary. (And when it was plain TCP, it *wasn't*
necessary, because TCP already implements this for you.)
> and ideally an
> erro-recovery mechanism where it will try to reconnect in case the
> keepalive gets eaten by goblins.
It appears to be impossible to tell under WCF when the connection has
been broken.
>> OK, so under Mono it appears to be _impossible_ to stop the server
>> disconnecting.
>
> At the risk of repeating myself... program an application-level keepalive.
The solution I eventually came up with was to connect, send one command,
and then immediately disconnect. So now the program makes a new
connection for every individual command. It's inefficient, but what else
can I do?
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 08/11/2013 07:48 AM, scott wrote:
> (not to mention relearning all the differences between C++ and C#).
Ooo, I can explain this one for you:
The difference between C++ and C# is... they bare absolutely no
resemblance to each other whatsoever!
Part of our code is C#, and a small part is C++. We try to avoid
touching the C++ part. (I especially enjoy the way it compiles
differently under different compilers... Oh, and manually editing
Makefiles is fun too.)
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
|
|