Identifying and managing clients in nonblocking NIO socket programming

Here's my beef. When I write a Server using a thread for every new connection that comes in, I can remember everything for each client in its respective thread. I can have a table entry in a MYSQL database for each client which each thread can look up by a remembered id. I can direct packets from a different application to a specific client by passing a message to the thread associated with the client. But, in this implementation, the client-server setup will need to have a master-slave relationship where one is always reading, and can talk on the network only in response.
I like the NIO selector way of doing things, but I do not get a memory method to deal with specific clients. How do I, for example,
direct packets to a specific client.?
recognize incoming packet info and make sense of its context in relation to past messages to and from the same client?
maintain one-to-one correspondence between a MYSQL database table entry and a specific client?
Could somebody explain this to me how I can maintain a client environment, and a client state machine with memory?
Thanks
Anil

cloud9ine wrote:
Here's my beef. When I write a Server using a thread for every new connection that comes in, I can remember everything for each client in its respective thread. I can have a table entry in a MYSQL database for each client which each thread can look up by a remembered id. I can direct packets from a different application to a specific client by passing a message to the thread associated with the client. But, in this implementation, the client-server setup will need to have a master-slave relationship where one is always reading, and can talk on the network only in response.
I like the NIO selector way of doing things, but I do not get a memory method to deal with specific clients. How do I, for example,
direct packets to a specific client.?
recognize incoming packet info and make sense of its context in relation to past messages to and from the same client?
maintain one-to-one correspondence between a MYSQL database table entry and a specific client?
Could somebody explain this to me how I can maintain a client environment, and a client state machine with memory?
Thanks
AnilYou will use select() and SelectionKey when you deal with NIO. The SelectionKey has an attach method that you can use to associate the client with something, e.g. a session. Your Session class can contain all that you now have in your thread.
Kaj

Similar Messages

  • ConfigMgr 2012 R2 and managing clients in untrusted forest

    I have read documentations and I'm still not 100% sure what are the possible limitations in my situation. I have 2 AD forests without any trusts between them. I'm planning to deploy ConfigMgr 2012 R2 in forest A. I also have clients in forest B.
    I need to install operating systems via PXE, applications and windows updates to clients in untrusted forest. I'm also planning to support internet clients. 

    You can manage clients in un-trusted forests. This blog is a good place to start.
    http://blogs.technet.com/b/manageabilityguys/archive/2012/09/05/system-center-2012-configuration-manager-and-untrusted-forests.aspx
    Managing internet clients is called IBCM (Internet Based Client Management). You can read about it here
    http://blogs.technet.com/b/configurationmgr/archive/2013/12/11/a-closer-look-at-internet-based-client-management-in-configmgr-2012.aspx
    Gerry Hampson | Blog:
    www.gerryhampsoncm.blogspot.ie | LinkedIn:
    Gerry Hampson | Twitter:
    @gerryhampson

  • Need help with java.nio.socket program.

    Can someone please help me here? I am trying to create a class that listens on a socket for an incoming connection, but I am getting the following error on compile:
    SocketIn.java:48: incompatible types
    found : java.nio.channels.SocketChannel
    required: java.net.Socket
    Socket requestSocket = requestChannel.accept();
    Here is my code:
    package NETC;
    import java.net.*;
    import java.util.*;
    import java.io.*;
    import java.nio.*;
    import java.nio.charset.*;
    import java.nio.channels.*;
    import java.nio.channels.spi.*;
    public class SocketIn{
         // Buffer for any incoming data.
         private static ByteBuffer inBuffer = ByteBuffer.allocateDirect(1024);
         CharBuffer charBuffer = null;
         public SocketIn(int intPort)throws Exception{
              // Create a non-blocking server socket.
              ServerSocketChannel serverChannel = ServerSocketChannel.open();
              serverChannel.configureBlocking(false);
              // Use the host and port to bind the server socket.
              InetAddress inetAddress = InetAddress.getLocalHost();
              InetSocketAddress socketAddress = new InetSocketAddress(inetAddress, intPort);
              serverChannel.socket().bind(socketAddress);
              // The selector for incoming requests.
              Selector requestSelector = SelectorProvider.provider().openSelector();
              // Put the server socket on the selectors 'ready list'.
              serverChannel.register(requestSelector, SelectionKey.OP_ACCEPT);
              while(requestSelector.select() > 0){
                   System.out.println("Connection Accepted...");
                   // A request has been made and is ready for IO.
                   Set requestKeys = requestSelector.selectedKeys();
                   Iterator iterator = requestKeys.iterator();
                   while(iterator.hasNext()){
                        SelectionKey requestKey = (SelectionKey)iterator.next();
                        // Get the socket that's ready for IO.
                        ServerSocketChannel requestChannel = (ServerSocketChannel)requestKey.channel();
                        // This shouldn't block.
                        Socket requestSocket = requestChannel.accept();
                        inBuffer.clear();
                        requestSocket.read(inBuffer);                    
                        inBuffer.flip();
                        Charset charset = Charset.forName("ISO-8859-1");
                        CharsetDecoder decoder = charset.newDecoder();
                        charBuffer = decoder.decode(inBuffer);
                        iterator.remove();                    
    }

    Well its been about a month and a half so I guess you have your answer. In case you don't...
    // This shouldn't block.
    Socket requestSocket = requestChannel.accept();
    The problem is quite simple. The ServerSocketChannel.accept() returns a SocketChannel. (Channel -> Channel).
    You can get access to the socket using the SocketChannel.
    SocketChannel requestSocketChannel = requestChannel.accept();
    Socket requestSocket = requestSocketChannel.socket();
    But you don't need the socket to read and write.

  • Managed Clients Printers and wrong Printernames

    Hi,
    we have managed clients and assign printers through the WGM.
    If for example the client uses the "ad dept"printer the printername in the dock, while it is printing says "finance dept" but prints to the correct printer.
    I have spoken with other admins, that also use WGM and managed clients/printers ad they have the same situation.
    Again: The detination is right but the name of the printer in the Dock is wrong.
    I looked in the detailview in WGM and found nothing irregular.
    Is there a way to handle this?
    regards
    Oliver

    We had a wierd problem with HP printers giving the wrong names... we had to change the printers mDNS name. Dunno if this helps

  • Socket programming usin nio package

    hello frens i m new to socket programming
    i come to know that nio packaage provides more sophisticated and efficient API to deal with socket programming issue
    I need to know where i can get tutorial about socket programming using nio pacakage

    Try google
    http://www.google.co.uk/search?q=java+nio+tutorial

  • Daemon and socket programming

    Hi,
    I have been given a new project whereby I have to:
    1) Write a class with a method that takes as an argument an xml document, communicate s with a SOAP server and return the SOAPs response (in this case a ResponseWrapper object).
    2) This then has to be implemented as a Daemon, that listens to a particular socket.
    The reason for this being is that it can be uploaded to a server, allowing a pearl program to send the XML to the Daemon, have this contact the SOAP server and then return the SOAPs response back to the pearl program.
    I am aware that this sounds unnecessarily complicated but it does have to be implemented this way.
    I have written the class and it successfully communicates with the SOAP server and returns a response, but have no idea how to implement it as a Daemon and have had no experience with socket programming.
    Could anyone point me to a tutorial or give me some pointers in how to get started?
    Thanks,

    It was an option to use a Web Service, but I was told
    that due to security issues it would take to long to
    write all the necessary checks and interceptors
    (which I know how to do), so I should implement it
    this way (which I don't know how to do); since it is
    sitting behind the firewall it would not be
    accessible for spoofing.Huh? You realize that tons of very sensitive information is transmitted all over the internet using Web Services right?
    I have had no experience
    with implementing this type of thing before, What is your level of experience then? This may be over your head. I'm not saying that sockets and daemons are advanced concepts, but I wouldn't throw a beginner into that task.

  • Can i run UDP  client and UDP  server socket program in the same pc ?

    hi all.
    when i execute my UDP client socket program and UDP server socket program in the same pc ,
    It's will shown the error msg :
    "Address already in use: Cannot bind"
    but if i run UDP client socket program in the remote pc and UDP server socket program run in local pc , it's will success.
    anybody know what's going on ?
    any help will be appreciated !

    bobby92 wrote:
    i have use a specified port for UDP server side , and for client define the server port "DatagramSocket clientSocket= new DatagramSocket(Server_PORT);"Why? The port you provide here is not the target port. It's the local port you listen on. That's only necessary when you want other hosts to connect to you (i.e. when you're acting as a server).
    The server should be using that constructor, the client should not be specifying a port.
    so when i start the udp server code to listen in local pc , then when i start UDP client code in local pc ,i will get the error "Address already in use: Cannot bind"Because your client tries to bind to the same port that the server already bound to.

  • Build and Capture TS fails in "Prepare Configuration Manager Client" task

    I have a ConfigMgr 2012 R2 + CU1 I use for Windows 7 deployment.
    I have made a "build and Capture" TS in ConfigMgr that I use to build my reference image.
    When I run the TS it fails at the "Prepare Configuration Manager Client" step where I get the following error:
    The task sequence execution engine failed executing the action (Prepare Configuration Manager Client) in the group (Capture the Reference Machine) with the error code 2147749938
    Action output: ... 1 instance(s) of 'SMS_MaintenanceTaskRequests' successful
    Successfully reset Registration status flag to "not registered"
    Successfully disabled provisioning mode.
    Start to cleanup TS policy
    getPointer()->ExecQuery( BString(L"WQL"), BString(pszQuery), lFlags, pContext, ppEnum ), HRESULT=80041032 (e:\nts_sccm_release\sms\framework\core\ccmcore\wminamespace.cpp,463)
    ns.Query(sQuery, &spEnum), HRESULT=80041032 (e:\qfe\nts\sms\framework\tscore\utils.cpp,3666)
    End TS policy cleanup
    TS::Utility::CleanupPolicyEx(false), HRESULT=80041032 (e:\nts_sccm_release\sms\client\osdeployment\preparesmsclient\preparesmsclient.cpp,564)
    pCmd->Execute(), HRESULT=80041032 (e:\nts_sccm_release\sms\client\osdeployment\preparesmsclient\main.cpp,136)
    Wmi query 'select *from CCM_Policy where PolicySource = 'CcmTaskSequence'' failed, hr=0x80041032
    Failed to delete policies compiled by TaskSequence (0x80041032)
    Failed to prepare SMS Client for capture, hr=80041032
    Failed to prepare SMS Client for capture, hr=80041032. The operating system reported error 2147942402: The system cannot find the file specified.
    If I disable the "Install Software Updates" part of my TS it will run without probems. It installs 154 updates during the build.
    I have seen another reference to this problem on this forum but why should the large number of the updates be the reason why the task sequcens cannot prepare the SCCM client for capture.
    Sounds to me that it's a BUG :-(
    Thomas Forsmark Soerensen

    I'm glad I'm not the only one running into this.  I've been racking my brain for the past few days trying to figure out where I went wrong. 
    SCCM 2012 R2 CU1
    Installation properties in TS: SMSMP=mp.f.q.d.n FSP=mp.f.q.d.n DNSSUFFIX=f.q.d.n PATCH="%_SMSTSMDataPath%\Packages\AP200003\hotfix\KB2882125\Client\x64\configmgr2012ac-sp1-kb2882125-x64.msp;%_SMSTSMDataPath%\Packages\AP200003\hotfix\KB2905002\Client\x64\configmgr2012ac-r2-kb2905002-x64.msp;%_SMSTSMDataPath%\Packages\AP200003\hotfix\KB2938441\Client\x64\configmgr2012ac-r2-kb2938441-x64.msp"
    Drop Windows 7 WIM that has all the updates Schedule Updates (offline servicing) installed (169 of 277)
    Apply several updates offline (the dual-reboot Windows 7 updates among others)
    Run Windows Updates more than once to be sure I get everything
    Breaks at the preparing client for capture step
    Is this:
    a bug?
    a known issue
    something that's just frowned upon for no technical reason?
    I'd love to hear from an SCCM guru [at Microsoft] on what the heck is going on here.

  • How can Webservice manage client sections and send back messages to clients

    Hi all.
    I am starting working with Webservice (jax-ws). Now i can make a good server side and on client side i can use method interface on webservice server side.
    But i want to make a advance feature as Server side manage client section and can send back message to client when Server change status...
    I know RMI and Corba can seems to support to send back messages to sections of cliens right ?
    So Webservice can do it ? Please help me !
    Thanks
    Diego
    Edited by: ThuCT on Sep 14, 2010 1:57 AM

    Hi all.
    I am starting working with Webservice (jax-ws). Now i can make a good server side and on client side i can use method interface on webservice server side.
    But i want to make a advance feature as Server side manage client section and can send back message to client when Server change status...
    I know RMI and Corba can seems to support to send back messages to sections of cliens right ?
    So Webservice can do it ? Please help me !
    Thanks
    Diego
    Edited by: ThuCT on Sep 14, 2010 1:57 AM

  • Use two streams to manager client video/audio,Can i combine streams and record it with server AS API

    Use two streams to manager client video/audio,Can i combine streams and record it with server AS API
    I tried Stream.play()
    var s=Stream.get("combine");
    s.play("video");
    s.play("audio",null,null,false);
    s.record("append");
    it's don't work!!

    Thanks, that's what I had thought. Our domain.com zone is sourced internally and replicated to our advertisers for external users, so there's no way to change the result for internal vs external users.
    This is a rudimentary question that I should already know but I sort of inherited this after it was built: Can I have users sign in with their email address ([email protected]) but under the hood, their SIP address is [email protected]? This would let users
    sign in with an address they expect, but it would take advantage of the the local lyncdiscoverinternal record.
    Thanks,
    Matt

  • Client Identifier and MAC addresses

    Hello Everyone,
    I am migrating my DHCP services from an old SUN server to an OES Linux server. In the old SUN files, my client identifier and mac addresses have a leading pair. So if my mac address is 00:30:C1:57:44:09, it is entered in the SUN files like this: 01:00:30:C1:57:44:09. My question is, do I need to enter that extra pair in my host records for OES Linux DHCP? If I do or don't, does it matter?
    The reason I'm asking is that many of my older printers and even some of the newer ones are not picking up an address from the new DHCP server when the old one expires.
    Thanks,
    Toney.

    On 21/06/2010 19:46, toneyc wrote:
    > I am migrating my DHCP services from an old SUN server to an OES Linux
    > server. In the old SUN files, my client identifier and mac addresses
    > have a leading pair. So if my mac address is 00:30:C1:57:44:09, it is
    > entered in the SUN files like this: 01:00:30:C1:57:44:09. My question
    > is, do I need to enter that extra pair in my host records for OES Linux
    > DHCP? If I do or don't, does it matter?
    Whilst I've seen this with NetWare-based DHCP the DHCP on OES Linux uses
    different objects and is provided by ISC DHCP which does not have this -
    my DHCP hosts are configured just with MAC address.
    If you examine a dhcpHost object in ConsoleOne - clicking the Other tab
    should show the dhcpHWAddress attribute has the value "ethernet
    01:23:45:67:89:ab" (where 01:23:45:67:89:ab is MAC address).
    > The reason I'm asking is that many of my older printers and even some
    > of the newer ones are not picking up an address from the new DHCP server
    > when the old one expires.
    Just printers or other types of devices too?
    If printers, particularly HP ones, check that they're set to pick up IP
    details via DHCP and not BOOTP. Yes should still work but we've seen
    problems here.
    HTH.
    Simon
    Novell Knowledge Partner (NKP)
    Do you work with Novell technologies at a university, college or school?
    If so, your campus could benefit from joining the Novell Technology
    Transfer Partners (TTP) group. See www.novell.com/ttp for more details.

  • Please clarify license about server managed and configuration manager client ML system center 2012

    Could clarify why managed servers License required ,already we bought the configuration manager client ML License ? We are planing to buy system center operation manager 2012

    Hello,
    if you wants planing to Monitoring your AD with Exchange Server and you have only one physical Server with for example 2 CPUs and 2 VMs, you need to purchase the System Center Standard 2012 R2 SML for Monitoring your Server environement. However,  if
    your AD is a datacenter or for example 4 x Windows Server Standard with 8 VMs than, you need to purchase the System Center datacenter SML.
    the System Center SML is licensed per physical Server: one license up to two physical CPUs. if you have for 2 physical Hosts with 2 CPUs each, you need to purchase two System Center licenses.
    the only difference between Standard and datacenter is only about virtualization rights
    for System Center Operation Manager for your Clients Desktop, you need to purchase the System Center Client Management Suite includes: Operation Manager; Services Manager; Data Protection Manager and Orchestrator.
    thanks
    diramoh

  • OSD with Configuration Manager Clients and PKI certifications

    My team and I had just set up PKI Certs on our System Center 2012 R2 Server. We want the opportunity to utilize Out Of Band Management, but we also would like to make our OSD available to the following "Only Configuration Manager Clients." I have
    done some testing and have found that without PKI enabled we have been able to use this feature. Once we enabled PKI we could only perform OSD through PXE or Boot Media.
    Is there a way to have PKI enabled but also allow OSD's to be available through "Only Configuration Manager Clients"?

    Thanks for the reply, I will relay the info concerning OOB. It is not necessarily a technical problem we are running into, more of an inconvenience. OSD works great as long as you make your TS deployment available through Boot media or PXE. Our end goal
    is to create a zero touch environment. To do this I was thinking of making the TS available only configuration manager clients. With PKI I loose that capability and it forces me to use boot media or PXE.
    Is there a way to have PKI enabled and still be able to deploy TS with only configuration manager clients, and no other deployment method?
    Thank you again.
     

  • NIO sockets

    Hello,
    Im having difficulty understanding the WRITE issue of nio sockets. Ive read most of the nio-related posts on this forum but I still cant figure it out. Ive also looked at some examples by PkWooster but they seems really complicated.
    I have a server which accepts connections and echos messages to all clients. Everything works fine except my client cannot write to the server. I know I need to switch back and forth between Op interests since they can block the cpu if set incorrectly but I dont really understand what to set when. Since my server seems to be working fine Ill post my Clients code:
    import java.io.*;
    import java.nio.*;
    import java.nio.channels.*;
    import java.net.InetSocketAddress;
    import java.util.*;
    import java.nio.charset.*;
    public class GameClient implements Runnable {
      Game game;
      SocketChannel sc;
      ByteBuffer buffer;
    Selector selector;
    ByteBuffer sendBuffer=null;
        String host = "192.168.1.102";
        int port = 8888;
    private CharsetDecoder asciiDecoder;
    private CharsetEncoder encoder;
    private LinkedList sendQ = new LinkedList();
    SelectionKey sk;
    LinkedList invocations;
    public GameClient (Game g) {
    game = g;
        buffer = ByteBuffer.allocateDirect(1024);
              Charset charset = Charset.forName("ISO-8859-1");
              asciiDecoder = charset.newDecoder();
              encoder = charset.newEncoder();
    game.CharMSGArrival("Starting...");  // transfers data to the game thread
    public void run(){
    try{
        // Create a nonblocking socket channel and connect to server.
        sc = SocketChannel.open();
        sc.configureBlocking(false);
    game.CharMSGArrival("Connecting to server..."+"\n");
        InetSocketAddress addr = new InetSocketAddress(host, port);
        sc.connect(addr);    // Nonblocking
        while (!sc.finishConnect()) {
    game.CharMSGArrival("I am waiting ..."+"\n" );
    game.CharMSGArrival("Connection acqired"+ "\n" );
        // Send initial message to server.
        buffer.put((new Date().toString()+ "\n").getBytes());
        buffer.flip();
        sc.write(buffer);
        // Create a new selector for use.
        selector = Selector.open();
        // Register the socket channel with the selector.
        sk = sc.register(selector, SelectionKey.OP_READ);
        while (true) {
            //System.out.println("Listening for server on port " +
             //                   remotePort);
            // Monitor the registered channel.
            int n = selector.select();
            if (n == 0) {
                continue;   // Continue to loop if no I/O activity.
            // Get an iterator over the set of selected keys.
            Iterator it = selector.selectedKeys().iterator();
            // Look at each key in the selected set.
            while (it.hasNext()) {
                // Get key from the selected set.
                SelectionKey key = (SelectionKey) it.next();
                // Remove key from selected set.
                it.remove();
                // Get the socket channel from the key.
                SocketChannel keyChannel = (SocketChannel) key.channel();
                // Is there data to read on this channel?
                if (key.isReadable()) {
                    replyServer(keyChannel);
                if (key.isWritable()) {
                    doSend();
           } catch (IOException ioe) {
      private void replyServer(SocketChannel socketChannel)
                          throws IOException {
        // Read from server.
    buffer.flip( );
        int count = socketChannel.read(buffer);
        if (count <= 0) {
            // Close channel on EOF or if there is no data,
            // which also invalidates the key.
            socketChannel.close();
            return;
    buffer.flip( );
    String str = asciiDecoder.decode(buffer).toString( );
         game.CharMSGArrival(str);
    public void doSend(){
    sk.interestOps(SelectionKey.OP_WRITE);     
    if(sendBuffer != null)write(sendBuffer);
    writeQueued();     
    public void trySend(String text){ // this method is invoked when a send buttong is pressed in the 'game' thread
              sendQ.add(text);     // first put it on the queue
              writeQueued();          // write all we can from the queue
         private void writeQueued()
              while(sendQ.size() > 0)     // now process the queue
                   String msg = (String)sendQ.remove(0);
                   write(msg);     // write the string
    private void write(String text)
              try
              ByteBuffer buf = encoder.encode(CharBuffer.wrap(text));
                   write(buf);
              catch(Exception e){e.printStackTrace();}
             * write out a byte buffer
         private void write(ByteBuffer data)
              SocketChannel scc = (SocketChannel)sk.channel();
              if(scc.isOpen())
                   int len=0;
                   if(data.hasRemaining())
                        try{len = sc.write(data);}
                        catch(IOException e){e.printStackTrace(); //closeComplete();
                   if(data.hasRemaining())     // write would have blocked
    game.CharMSGArrival("write blocked"+"/n");
                        //writeReady = false;
         sk.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);     // select OP_WRITE
                        sendBuffer = data;          // save the partial buffer
                   else {sendBuffer = null;
    sk.interestOps(SelectionKey.OP_READ);     }
              else game.CharMSGArrival("write closed"+"/n");
    }

    public void doSend(){
    sk.interestOps(SelectionKey.OP_WRITE);     
    if(sendBuffer != null)write(sendBuffer);
    writeQueued();     
    }This method makes no sense. You only get into it if the key is writable, which means it is already registered for OP_WRITE, so why would you register it again?
    Also you're frequently ignoring the result of many write calls, of which you have too many.
    See the thread 'Taming the NIO circus': http://forum.java.sun.com/thread.jspa?threadID=459338,
    ignoring all the contributions except those by pkwooster and me.
    I'm not that crazy about Mina myself. It has its own buffer classes and it quietly uses threads, i.e. missing the entire point of NIO.

  • NIO sockets - cant get it to block!

    Hi,
    I'm trying to use NIO sockets in a program which sends data to a socket on another machine. I want it to block so that it must wait for a response before it continues. However this doesnt happen! I used Ethereal to see the packets being sent and recieved and the socket clearly doesnt block. Am I correct in thinking that NIO sockets are configured to block by default? I even tried using the configureBlocking(true) method to make sure but it still didnt block. Can anyone shed some light onto why this could be happening? Has this happened to anyone else?
    Cheers

    thomfost wrote:
    I've tried using regular I/O but its a bit too slow for what I need, im trying to see if using NIO improves this.Well you can stop now because it won't. NIO, as mentioned, is non-blocking IO, so a thread that is titled "NIO sockets - cant get it to block!" is not ultra promising. More importantly NIO will not make your IO "faster" by some magic. That's not what it does. It helps you write programs that scale because you don't need to have a thread dedicated to every client who connects to your system.
    At any rate you have multiple mistakes here which suggest the other slowness problem you refer to is a logical problem in your code. So why don't you tell us about that instead?

Maybe you are looking for

  • How to set the "delete" button in Mail to actually delete instead if Archive Google Mail messages...

    I know these kinds of questions are asked a lot, but I just wanted to ask my specific question for a nice specific answer. I understand Gmail's thoughts on archiving and labels, but it makes gmail a beast to work with on mail clients.  I FINALLY have

  • Bluetooth not available error

    I got a early 2008 MAC PRO with additional wireless keyboard and mighty mouse few weeks ago. Recently I keep encountering the "bluetooth not available" problem after software update (system reboot) if I have USB devices attached to the MAC PRO. Also,

  • Lion has crashed my MacBook Pro

    Help!!! I did an upgrade to Lion tonight. Did a back up to Time Capsule prior to download. Download, install and crash. I get an error message that the hard drive is "locked" and cannot be upgraded. I tried to restore a back up...states file is corru

  • Fixed No of rows in the table

    Hi, I have the below xml structure :- CTRLS CTRL PYTO STMENTS STMENT ( company name , company address) POS ( po number , po balance amt) INVs INv     ( invoice number , invoice amount , invoice type ) Every CTRLS will have multiple CTRL and every CTR

  • Creating an iPhoto project book....how can I add photos to the photo pane?

    I am creating a book from our recent vacation. Each day of the vacation I have separated into different Events. I chose the first day/event and clicked create book.....have placed all the photos in various "slots" in the book and now want to add phot