One way is web services. I've written Java JAX-RPC clients which talk to a J2EE Container's JSE (JAX-RPC service endpoint), and you can also have non-Java clients. The container vendor implements JAX-RPC servlets for the JSE, so the JSE runs in a Servlet container.
Let's say you are invoking methods on generated stubs. Based on the contents of a WSDL description of a service, a SOAP toolkit can be used to generate the stubs. (You'll have to do some research to establish what toolkits are available, I only know about how WebLogic Server generates the stubs.) These generated stubs are configured with all necessary information about the Web service and its endpoint. The client application uses the stubs to invoke remote methods available in the Web service endpoint.
The EJB 2.1 spec introduced another endpoint, the EJB endpoint. This endpoint is a stateless session bean which implements all the methods of the endpoint interface, which is a subinterface of either java.rmi.Remote or javax.ejb.EJBObject. You will still need a SOAP toolkit to generate the appropriate service interfaces to the Web service. The Web service client can then send a SOAP request to the EJB container which hosts the EJB endpoint.
Basically you just open streams and send bytes. Each side has to agree what the bytes mean, so it takes some planning. I did a Java search engine talking to Perl program clients and sending binary data over permanently open socket connections - the receiving side had to know when a block of data was complete so each communication started with a byte count for the data block to follow. We also had to cope with the way each machine used byte order in representing ints. Bill