[kamaelia-list] Re: Small mod to console example doesn't work?
Ah, I forgot the original context you were working in! I did too :) I didn't communicate it clearly, I think. I was trying to accomplish too much too quickly. This is exactly what I hoped for, a solution that would not force me to disconnect and reconnect. Let me give this a go and get back to you tonight EST, which is your morning tomorrow. Thanks again! Gloria All the suggestions I've made previously could be considered as trying to this problem: that the ConsoleReader component not only reads user input, but also generates a prompt for it (which it directly writes to the console (standard output). It sounds like what you actually are after building is a simple chat protocol - abstracted away from having to know about where its input is coming from or where its output is going to. The ConsoleReader component mixes a bit of both - it handles the sourcing of input, but also generates some output itself (the prompt) and worse still, it puts this output onto the console directly! :-) So what you're actually after is what you originally had: Pipeline( SourceOfInput(), MyComponent(), DestinationForOutput() ) So for testing your chat protocol component MyComponent - we can still use ConsoleReader and ConsoleEchoer. We just have to ignore the '' prompt generated by the ConsoleReader and pretend its not there. When you plug Mycomponent into the ConnectedServer (also known as SimpleServer in more recent subversion copies of Kamaelia iirc) as its protocol, it gets wired up in much the same way as above - data coming from the client will get send to its inbox and data send out of its outbox will be sent back to the client. So all of the Seq stuff is not strictly necessary. All you need to do is encode the sequential behaviour into the main() generator in MyComponent. For example something along the lines of this (note the shutdown handling makes it a little messier that it might otherwise be): def main(self): mustStop = false; # 1) generate login prompt self.send(Enter username:, outbox) # 2) wait for response mustStop = self.doShutdown() while not mustStop and not self.dataReady(inbox): self.pause() yield 1 mustStop = self.doShutdown() if mustStop: return username = self.recv(inbox) # 3) welcome self.send(Welcome +username+! Begin chatting!) # 4) do chatting ... One other thing to consider is that the ConsoleReader specifically buffers input until a whole line is received from the user, before outputting that as a single string message to its outbox outbox. Whereas the socket connection may send fragments of strings before a whole line has been entered. A component such as this one can sort that out for you: http://www.kamaelia.org/Components/pydoc/Kamaelia.Visualisation.PhysicsGraph.chunks_to_lines.html Simply pipeline it with MyComponent and make that the protocol thats given to the ConnectedServer: def myProtocol(): return Pipeline( chunks_to_lines(), MyComponent() ) ... ConnectedServer(protocol=myProtocol, port=WHATEVER).run() Matt On Tue, 03 Mar 2009 04:35:07 -, Gloria W strang...@comcast.net wrote: I got the carousel example working, but it sends/receives from/to the original console, not any new client which connects. I realized I can reuse the ConsoleEchoer, but somehow make it send a string to a client upon connect, and have the server process the string instead of echoing it to the server's console. I tried this, but all proposed solutions so far write to the console, not a newly connected client. Maybe I am missing something, btu I think I am close. Aside from this, I was wondering if a carousel or seq will be appropriate to solve my problem. I am trying to simulate what happens in crude chat systems such as IRC. For simplicity, in this example the user connects, is prompted for a name, and then can chat freely. If I have two pipelines: one detects client connect and is prompting for a name, receiving the response, and then handing control over to another pipeline; doesn't this require a disconnect/reconnect with the client? If so, this would be bad, because the user would have to re-telnet in, would not be logged in, and would have to start again. So I guess if I get the data flow working, I can write this logic in one pipeline, and reuse the same connection, assuming the first thing a user types after connecting would be the name. Or maybe there is a clever way to hand off a connected pipline to another utility, so that my flow control is cleaner? (not running an if name is set check upon each message). Thanks again for your help, Gloria This is really
[kamaelia-list] Re: Small mod to console example doesn't work?
Matt, thanks for this response. It took me a while to get through it and make sure I understood each point. I appreciate it, comments are below. This is true, thanks. Now let's say I wanted to write to the client's console before reading anything from their console. With no other changes, this Pipeline setting doesn't work: Pipeline( MyComponent(), ConsoleReader(), ).run() but I am expecting it to work. What am I missing? Pipeline isn't about saying what order to run things in - its more like expressing how to plug things together: The declaration above is saying: Make two components - an instance of MyComponent and an instance of ConsoleReader; now plug them together so that the outbox of MyComponent is joined to the inbox of ConsoleReader I was assuming an order of operations here, Thanks for the correction. It makes sense. This is true, thanks. Now let's say I wanted to write to the client's console before reading anything from their console. So you want a program where the flow of data is as follows: input from console --- MyComponent output to console I guess it would be more like: send a one-time prompt for input to the client's console --- read client's response and send to MyComponent then create the flow you specified: input from console --- MyComponent output to console Imagine, for example, taking the SimpleChatServer example, and sending an Enter your chat name prompt, getting a response, and THEN connecting the user to the server. [snip] Thats the simple version. Now a little more complexity! ... The above code won't guarantee is whether the initial message for the console is displayed before the prompt that is displayed by the ConsoleReader. Why is this? Because the Pipeline declaration effectively says make all these and start them all at teh same time. Got it. Thanks. This helped greatly. If you want explicit behaviour where a component isn't created or activated, or wired up until a later point in the execution (ie. you want to control the order things happen in) then you need to express it explicitly. Here's *a* way to do it: We can use the Carousel component do delay the creation and wiring up of the ConsoleReader component until MyComponent sends a message telling it to do so. There's some explanation of Carousel here: http://www.kamaelia.org/Cookbook/Carousels if it helps. In short, Carousel is designed to contain another component. When it receives a message, it calls a helper function (that you supply) to create a component. It then takes that component and wires it up so that its outbox and inbox are mapped to the outbox and inbox of the Carousel component itself. We'll declare this helper globally for now: def makeConsoleReader(message): # we ignore the parameter return ConsoleReader() So that MyComponent can send a message to the Carousel, we'll need to add an extra outbox to MyComponent, and get it to send that signal: Class MyComponent(component): Outboxes = {outbox : some data out, signal : Shutdown signal, ready : Sends a message when initialisation is complete, } ... def main(self): self.send(Initial message for the console, outbox) yield 1 self.send(OK, ready) while not self.doShutdown(): if self.dataReady(inbox): data = self.recv(inbox) # let's echo what we received... self.send(data, 'outbox') if not self.anyReady(): self.pause() yield 1 Note the 'yield' statements - which give other components a chance to run - giving the ConsoleEchoer a chance to display that initial message. Makes sense, thanks! Remember that we don't control the precise order in which components execute: if we sent the initial message and the ready message without a yield in between, we might be unlucky, and the Carousel might get executed first! We cannot use a simple pipeline now, since we need to also wire up the ready outbox of MyComponent to the Carousel's next inbox. A Pipeline only wires up the standard inbox and control inboxes and outbox and signal outboxes. So we'll use a Graphline instead, where we specify each linkage explicitly: from Kamaelia.Chassis.Carousel import Carousel from Kamaelia.Chassis.Graphline import Graphline Graphline( READER = Carousel(makeConsoleReader), MYCOMP = MyComponent(), OUTPUT = ConsoleEchoer(), linkages = { # main flow of data: (READER, outbox) : (MYCOMP, inbox), (MYCOMP, outbox) : (OUTPUT, inbox), # the 'ready' message:
[kamaelia-list] Re: Small mod to console example doesn't work?
On Mar 2, 8:36 pm, Gloria W strang...@comcast.net wrote: Matt, thanks for this response. It took me a while to get through it and make sure I understood each point. I appreciate it, comments are below. No problem. I've just realised another way to achieve the same effect that is probably simpler! There *is* a component that is all about getting things done in an order: the Seq component (The name is a homage to the OCCAM language): http://www.kamaelia.org/Components/pydoc/Kamaelia.Chassis.Seq.html With Seq, you provide a list of components. Seq goes through the list, starting with the first component. It activates it and wires up its inboxes and outboxes so that they're mapped to the Seq component's inboxes and outboxes (just like the Carousel does). When that component finishes, it then unwires it and moves onto the next in the list. It therefore effectively runs them *in sequence* one after the other. We've got two steps: displaying the initial message, then taking user input. Both steps need their output to be displayed. So we'll create a Seq componet, Pipeline'd into a console echoer: from Kamaelia.Chassis.Seq import Seq Pipeline( Seq( ... args to be determined! ... ), ConsoleEchoer() ).run() We can use a OneShot component (see http://www.kamaelia.org/Components/pydoc/Kamaelia.Util.OneShot.html ) to send that initial message, so that it gets sent to the ConsoleEchoer and displayed. OneShot, rather conveniently, then immediately terminates; so the Seq component will move onto the next component in its list. That next component can be the Pipeline of a ConsoleReader, sending its output into MyComponent. So what we have is: from Kamaelia.Chassis.Seq import Seq from Kamaelia.Util.OneShot import OneShot Pipeline( Seq( OneShot(initial message to be displayed), Pipeline( ConsoleReader(), MyComponent() ) ), ConsoleEchoer() ).run() In this case, MyComponent doesn't need to bother to send the initial message itself, as it did in the suggestion I previously made. So what happens here? Well, when the system starts running, initially the Seq component selects the first item in its list - the OneShot component. So, what actually ends up getting wired up is something like this: Pipeline( OneShot(initial message to be displayed), ConsoleEchoer() ) Its not actually exactly like that, because the OneShot is still contained within the Seq component - but because all the inboxes and outboxes are mapped to that of the Seq component, it is roughly equivalent. Then when the OneShot component has sent its message and terminated, the Seq component swaps it out and replaces it with the next thing in its list - the pipeline of a ConsoleReader and MyComponent. So the way the system is wired up suddenly switches to something akin to this: Pipeline( Pipeline( ConsoleReader(), MyComponent() ), ConsoleEchoer() ) Again, the inner pipeline is, in reality, contained within the Seq component, but it behaves roughly like it is shown. Matt --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups kamaelia group. To post to this group, send email to kamaelia@googlegroups.com To unsubscribe from this group, send email to kamaelia+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/kamaelia?hl=en -~--~~~~--~~--~--~---
[kamaelia-list] Re: Small mod to console example doesn't work?
This is really cool, and exactly what I need. I will get some time later to implement this and try it out, and I am sure I'll have more questions and comments. Thanks again for this. Gloria On Mar 2, 8:36 pm, Gloria W strang...@comcast.net wrote: Matt, thanks for this response. It took me a while to get through it and make sure I understood each point. I appreciate it, comments are below. No problem. I've just realised another way to achieve the same effect that is probably simpler! There *is* a component that is all about getting things done in an order: the Seq component (The name is a homage to the OCCAM language): http://www.kamaelia.org/Components/pydoc/Kamaelia.Chassis.Seq.html With Seq, you provide a list of components. Seq goes through the list, starting with the first component. It activates it and wires up its inboxes and outboxes so that they're mapped to the Seq component's inboxes and outboxes (just like the Carousel does). When that component finishes, it then unwires it and moves onto the next in the list. It therefore effectively runs them *in sequence* one after the other. We've got two steps: displaying the initial message, then taking user input. Both steps need their output to be displayed. So we'll create a Seq componet, Pipeline'd into a console echoer: from Kamaelia.Chassis.Seq import Seq Pipeline( Seq( ... args to be determined! ... ), ConsoleEchoer() ).run() We can use a OneShot component (see http://www.kamaelia.org/Components/pydoc/Kamaelia.Util.OneShot.html ) to send that initial message, so that it gets sent to the ConsoleEchoer and displayed. OneShot, rather conveniently, then immediately terminates; so the Seq component will move onto the next component in its list. That next component can be the Pipeline of a ConsoleReader, sending its output into MyComponent. So what we have is: from Kamaelia.Chassis.Seq import Seq from Kamaelia.Util.OneShot import OneShot Pipeline( Seq( OneShot(initial message to be displayed), Pipeline( ConsoleReader(), MyComponent() ) ), ConsoleEchoer() ).run() In this case, MyComponent doesn't need to bother to send the initial message itself, as it did in the suggestion I previously made. So what happens here? Well, when the system starts running, initially the Seq component selects the first item in its list - the OneShot component. So, what actually ends up getting wired up is something like this: Pipeline( OneShot(initial message to be displayed), ConsoleEchoer() ) Its not actually exactly like that, because the OneShot is still contained within the Seq component - but because all the inboxes and outboxes are mapped to that of the Seq component, it is roughly equivalent. Then when the OneShot component has sent its message and terminated, the Seq component swaps it out and replaces it with the next thing in its list - the pipeline of a ConsoleReader and MyComponent. So the way the system is wired up suddenly switches to something akin to this: Pipeline( Pipeline( ConsoleReader(), MyComponent() ), ConsoleEchoer() ) Again, the inner pipeline is, in reality, contained within the Seq component, but it behaves roughly like it is shown. Matt --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups kamaelia group. To post to this group, send email to kamaelia@googlegroups.com To unsubscribe from this group, send email to kamaelia+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/kamaelia?hl=en -~--~~~~--~~--~--~---
[kamaelia-list] Re: Small mod to console example doesn't work?
I got the carousel example working, but it sends/receives from/to the original console, not any new client which connects. I realized I can reuse the ConsoleEchoer, but somehow make it send a string to a client upon connect, and have the server process the string instead of echoing it to the server's console. I tried this, but all proposed solutions so far write to the console, not a newly connected client. Maybe I am missing something, btu I think I am close. Aside from this, I was wondering if a carousel or seq will be appropriate to solve my problem. I am trying to simulate what happens in crude chat systems such as IRC. For simplicity, in this example the user connects, is prompted for a name, and then can chat freely. If I have two pipelines: one detects client connect and is prompting for a name, receiving the response, and then handing control over to another pipeline; doesn't this require a disconnect/reconnect with the client? If so, this would be bad, because the user would have to re-telnet in, would not be logged in, and would have to start again. So I guess if I get the data flow working, I can write this logic in one pipeline, and reuse the same connection, assuming the first thing a user types after connecting would be the name. Or maybe there is a clever way to hand off a connected pipline to another utility, so that my flow control is cleaner? (not running an if name is set check upon each message). Thanks again for your help, Gloria This is really cool, and exactly what I need. I will get some time later to implement this and try it out, and I am sure I'll have more questions and comments. Thanks again for this. Gloria On Mar 2, 8:36 pm, Gloria W strang...@comcast.net wrote: Matt, thanks for this response. It took me a while to get through it and make sure I understood each point. I appreciate it, comments are below. No problem. I've just realised another way to achieve the same effect that is probably simpler! There *is* a component that is all about getting things done in an order: the Seq component (The name is a homage to the OCCAM language): http://www.kamaelia.org/Components/pydoc/Kamaelia.Chassis.Seq.html With Seq, you provide a list of components. Seq goes through the list, starting with the first component. It activates it and wires up its inboxes and outboxes so that they're mapped to the Seq component's inboxes and outboxes (just like the Carousel does). When that component finishes, it then unwires it and moves onto the next in the list. It therefore effectively runs them *in sequence* one after the other. We've got two steps: displaying the initial message, then taking user input. Both steps need their output to be displayed. So we'll create a Seq componet, Pipeline'd into a console echoer: from Kamaelia.Chassis.Seq import Seq Pipeline( Seq( ... args to be determined! ... ), ConsoleEchoer() ).run() We can use a OneShot component (see http://www.kamaelia.org/Components/pydoc/Kamaelia.Util.OneShot.html ) to send that initial message, so that it gets sent to the ConsoleEchoer and displayed. OneShot, rather conveniently, then immediately terminates; so the Seq component will move onto the next component in its list. That next component can be the Pipeline of a ConsoleReader, sending its output into MyComponent. So what we have is: from Kamaelia.Chassis.Seq import Seq from Kamaelia.Util.OneShot import OneShot Pipeline( Seq( OneShot(initial message to be displayed), Pipeline( ConsoleReader(), MyComponent() ) ), ConsoleEchoer() ).run() In this case, MyComponent doesn't need to bother to send the initial message itself, as it did in the suggestion I previously made. So what happens here? Well, when the system starts running, initially the Seq component selects the first item in its list - the OneShot component. So, what actually ends up getting wired up is something like this: Pipeline( OneShot(initial message to be displayed), ConsoleEchoer() ) Its not actually exactly like that, because the OneShot is still contained within the Seq component - but because all the inboxes and outboxes are mapped to that of the Seq component, it is roughly equivalent. Then when the OneShot component has sent its message and terminated, the Seq component swaps it out and replaces it with the next thing in its list - the pipeline of a ConsoleReader and MyComponent. So the way the system is wired up suddenly switches to something akin to this: Pipeline( Pipeline( ConsoleReader(), MyComponent() ), ConsoleEchoer() ) Again, the inner pipeline is, in reality, contained within the Seq component, but it behaves roughly like it is shown. Matt
[kamaelia-list] Re: Small mod to console example doesn't work?
On Sunday 01 March 2009 06:51:09 Gloria W wrote: Hi all, I am testing things, trying to get a small modification to this example working correctly: http://www.kamaelia.org/Home By this, I'm assuming you mean this: http://pastebin.com/m529b7499 I've simple added this line: self.send(Testing..1.2.3...,'outbox') between these two lines: while not self.doShutdown(): if self.dataReady(inbox): Like this? http://pastebin.com/m70a968b7 ie (diff) : http://pastebin.com/pastebin.php?diff=m70a968b7 and it does nothing. yielding it also does nothing. I should be able to write to the console without receiving data from it, I hope. What am I missing? I'm not sure. The pastebin above works. (floods the screen with Testing..1.2.3... ) Also, this is interesting: ChatServer(port=1501).run() ... /usr/lib/python2.5/site-packages/Kamaelia/Internet/TCPServer.py, line 144, in makeTCPServerPort s.bind((HOST,PORT)) File string, line 1, in bind socket.error: (98, 'Address already in use') It locks the port for about a minute after every run, then finally frees it, this error goes away, and I can finally reconnect. That's normal and part of the TCP stack's normal behaviour. In that 60 seconds, if you quickly type: netstat -natp |grep TIME_WAIT You'll see a line like this: tcp0 0 127.0.0.1:1500 127.0.0.1:48846 TIME_WAIT From this diagram: http://ssfnet.org/Exchange/tcp/Graphics/tcpStateDiagram1.gif You'll see that if the server side ends up in this state (bottom middle of the diagram) that it's expected to wait for 2 segment lifetimes ... which is your 60 seconds. Can you change this behaviour ? Yes. 2 ways: 1) on the system globally. Generally considered a bad idea, unless you really know what you're doing. 2) Change the behaviour of the server to say yes, allow this port to be reused, even within the 2 segment lifetimes. The latter you do by changing the socket options. Assuming you've done this: class ChatServer(ServerCore): You add in this: class ChatServer(ServerCore): socketOptions=(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) Alternatively, if you don't want change the ChatServer class definition, you can change this line: ChatServer(port=1501).run() To: ChatServer(port=1501, socketOptions=(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ).run() One thing I've thought about several times is whether this reuse of the socket address should be the default or not. There are pros and cons. Pros: 1 Don't have this explanation periodically :) 2 Allows instant server respawn 3 Simplifies many setups Cons: 1 When it goes wrong harder to explain why :) 2 Instant server respawn means that clients connected which are expecting segments can get confused. Generally this means they'll get a socket error and need to reconnect. That said, if the server restarts, that'd be necessary anyway, with or without the MSL behaviour. - Specifically what's happening in this scenario is the server is shutting down without the clients being disconnected, leaving each connection in an unexpected state, hence the TIME_WAIT state on the server socket. 3 Need a way of switching off this behaviour. Personally I think pro.3 outweighs everything else, but I don't generally like reusing default behaviours. Thanks in advance for your help! You're welcome :) Michael. -- http://yeoldeclue.com/blog http://twitter.com/kamaelian http://www.kamaelia.org/Home --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups kamaelia group. To post to this group, send email to kamaelia@googlegroups.com To unsubscribe from this group, send email to kamaelia+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/kamaelia?hl=en -~--~~~~--~~--~--~---