With just two players, use the following pattern (this is the simplest solution, but not the most intelligent - more intelligent methods aren't appropriate for such a simple case).
One client opens a ServerSocket, and dedicates a Thread to:
- listening to the ServerSocket (SS)
- Fetching a Socket from the SS when an incoming connection occurs.
- ...plus all the stuff we're about to do with the Socket
Each client dedicates two threads (the first client above can have one of these as a re-use of the SS thread it already has) to the Socket.
For each client, one thread is attached to the Socket.getInputStream(), the other on Socket.getOutputStream(). The Threads have various public methods like [for a thread on an OutputStream:] "sendTextMessage( String message )" or [for a thread on an InputStream] "addStreamListener( StreamListener )".
Note: the StreamListener is a class (or preferably an interface) you write that has methods like "receivedTextMessage( String message)". If it's an interface, you could have your GUI implement it, and whenever "receivedTextMessage" is called, it could update a JList of received messages, and call repaint().
That's the process, hopefully it makes sense? I'm half-asleep right now, and have checked what I wrote, and it seems sensible, but I'm too tired to know for sure

.