Reputation: 15146
I don't think i'm fully understanding the concept of having multiple clients connect to one server. I've seen it done so many different ways, heard how it works in so many different ways..
From what I'm understanding, each time the ServerSocket gets a connection from the client socket, it creates a new Socket so it can keep listening.
When I see people write it in code (sever-side), they always use one socket. I've been doing it that way ever since, but still haven't made progress.
My pal wrote the client, which works with the server, but we have problems with having the server display the message globally. This is how I have it structured (First 3 are for Server, last one is for Client:
Server.java
package Main;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import Streams.Stream;
public class Server {
public static final int maxConnections = 10;
ServerSocket serverSocket;
Socket socket;
User[] users = new User[maxConnections];
public Server() {
try {
serverSocket = new ServerSocket(43594);
while(Stream.streams < maxConnections) {
socket = serverSocket.accept();
for(User user : users) {
if(user == null) {
user = new User(socket);
Thread t = new Thread(user);
t.start();
System.out.println("Someone has joined the chat!");
return;
}
}
}
}catch(IOException e) { e.printStackTrace(); }
}
public static void main(String[] args) {
new Server();
}
}
User.java
package Main;
import java.io.IOException;
import java.net.Socket;
import Streams.Stream;
public class User implements Runnable {
Stream stream;
public User(Socket socket) {
stream = new Stream(socket);
}
public void run() {
String textInput, textOutput;
while(stream.exists()) {
try{
textInput = (String) stream.recieveData();
}catch(IOException e) {
e.printStackTrace();
}catch(ClassNotFoundException e) { e.printStackTrace(); }
}
}
public void sendMessage(String message) throws IOException {
stream.sendData(message);
}
}
Stream.java
package Streams;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class Stream {
public static int streams = 0;
Socket socket;
ObjectInputStream input; ObjectOutputStream output;
Object data;
public Stream(Socket userSocket) {
streams++;
socket = userSocket;
try{
input = new ObjectInputStream(userSocket.getInputStream());
output = new ObjectOutputStream(userSocket.getOutputStream());
}catch(IOException e) { e.printStackTrace(); }
}
public void sendData(Object data) throws IOException {
output.writeObject(data);
output.flush();
}
public Object recieveData() throws IOException, ClassNotFoundException {
return data = input.readObject();
}
public boolean exists() {
if(socket.isClosed()) return false; else return true;
}
}
Client.java
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Client extends JFrame {
private JTextField userText;
private JTextArea chatWindow;
private ObjectOutputStream out;
private ObjectInputStream in;
private String message = "";
private String serverIP;
private Socket clientSocket;
private int port = 43594;
Boolean CNC = false;
//constructor
public Client(String serverIP) {
super("Client Chat");
this.serverIP = serverIP;
userText = new JTextField();
userText.setEditable(false);
userText.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
if(userText.getText().length() > 0) {
sendMessageToServer(userText.getText());
userText.setText("");
}
}
}
);
add(new ClientMenu(), BorderLayout.NORTH);
add(userText, BorderLayout.SOUTH);
chatWindow = new JTextArea();
chatWindow.setEditable(false);
add(new JScrollPane(chatWindow), BorderLayout.CENTER);
add(new JScrollPane(new ClientTable()), BorderLayout.EAST);
setSize(600, 300);
setVisible(true);
}
//connect to server
public void startRunning() {
try{
connectToServer();
setupStreams();
whileChatting();
} catch(EOFException eofException) {
showMessage("\n Client terminated the connetion");
}catch(IOException ioException) {
ioException.printStackTrace();
}finally{
closeCrap();
}
}
//connect to server
private void connectToServer() {
showMessage("Attempting to connect to server... \n");
try {
clientSocket = new Socket(serverIP, port);
} catch (UnknownHostException e) {
CNC = true;
e.printStackTrace();
} catch (IOException e) {
CNC = true;
e.printStackTrace();
}
//showMessage("Connected to:" + connection.getInetAddress().getHostName());
}
//setup streams to send and receive messages
private void setupStreams() {
try {
out = new ObjectOutputStream(clientSocket.getOutputStream());
out.flush();
in = new ObjectInputStream(clientSocket.getInputStream());
showMessage("Stream established! \n");
showMessage("Use ::setname to change your name \n");
}catch(IOException e) { e.printStackTrace(); }
}
//while chatting with server
private void whileChatting() throws IOException{
ableToType(true);
do{
try{
message = (String) in.readObject();
showMessage("\n" + message);
}catch(ClassNotFoundException classNotfoundException) {
showMessage("\n ERROR! Message cannot be read");
}
}while(!message.equals("SERVER - END"));
}
//close the streams and sockets
private void closeCrap(){
if(CNC) {
showMessage("ERROR! Could not connect to server");
} else {
showMessage("\n Ending connections...");
ableToType(false);
try{
out.close();
in.close();
clientSocket.close();
}catch(IOException ioException) {
ioException.printStackTrace();
}
}
}
private void sendMessageToServer(String message) {
try{
out.writeObject(message);
out.flush();
}catch(IOException ioException) {
chatWindow.append("\n ERROR! Could not send message!");
}
}
//change/update chatWindow
private void showMessage(final String message) {
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
chatWindow.append(message);
}
}
);
}
//gives user permission to enter messages into text box
private void ableToType(final boolean tof) {
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
userText.setEditable(tof);
}
}
);
}
public static void main(String[] args) {
Client c = new Client("thisisatestip.zapto.org");
c.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
c.setLocationRelativeTo(null);
c.startRunning();
}
}
I was thinking that instead of making the ServerSocket static, then call User's constructor like
User(new Socket())
and accept the connection in the User class. Please let me know
Upvotes: 0
Views: 795
Reputation: 3288
Let me preface this with the fact that I haven't worked in Java in a long time, but I just wrote a simple client/server chat program in C#. Hopefully the concepts are all still the same.
A few things I'm noticing about your Server class :
You "return" from the constructor. Not sure if this is legal in Java (or a good idea), but the way it looks, it will shut down the server as soon as a client connections. For example: client makes connection, server assigns "user" to array, server returns from constructor, main exits, program terminates. I'd suggest having your constructor get everything set up, then having a "start" method that loops infinitely and adds new clients. In native code (C/C++) "accept()" blocks - I'm not sure if this is the case in Java but you can use it to your advantage here. Given this you could also use a dynamically sized array (ArrayList I think?) so you aren't limited to 10 clients. So, get connection, create user object with Socket, let that user do it's thing, loop and wait for another client connection.
You create a thread for each user object. This is probably not a good idea as the overhead that comes with having a bunch of concurrent threads will drastically reduce the performance of your server. What I did in my chat implementation is created a threadpool on the server for receiving data from clients and created a worker thread each time a client sent data to the server and the server received it. I used callback functions (not sure if there is anything like this in Java) and non-blocking operations on the server side so that I didn't have to have a dedicated thread for each client.
Given that you're using threads you're going to want to lock around important data. Locks serialize access to resources by preventing two (or more) threads from simultaneously attempting to access resources.
As for your original question, I would suggest the following: accept the connection on the server side, get a created Socket for the client from that, and pass it to the user object.
Upvotes: 2