Hot questions for Using Transmission Control Protocol in chat

Question:

I would like to create a simple TCP chat with option to send files. Sending messages to other clients works, but sending files doesn't work. My chat sends only few bytes of file.

Chat works like: Server starts up and wait for clients, Clients connect to server and they can send messages to other clients through the server. I would like to allow to the same with files.

Server is wrote in C, and Client is wrote in Java (I had guidelines like that).

Server:

    for (i = 0; i < max_clients; i++) {
        sd = client_socket[i]; 

        memset(buffer, 0, 10000);
        if (FD_ISSET( sd , &readfds)) {
            if ((valread = read( sd , buffer, 1024)) == 0) {
                getpeername(sd, (struct sockaddr*)&address, (socklen_t*)&addrlen); 
                printf("Host disconnected , ip %s , port %d \n" , 
                    inet_ntoa(address.sin_addr) , ntohs(address.sin_port)); 



                close( sd ); 
                client_socket[i] = 0; 
            } 
            else {
                // When message "start" arrived download the file and send it back to other clients
                if (strcmp(buffer, "start") == 0) {
                    uint8_t buff[10000];
                    // Read chunks of file
                    while (read( sd , buff, sizeof(buff)) > 0) {

                        // Sending chunks of file to other clients
                        for(j=0; j<max_clients; j++) {
                            int outSock = client_socket[j];
                            if(outSock != master_socket && outSock != sd) {
                                send(outSock , buff , sizeof(buff) , 0 ); 
                            }
                    }

                    }
                } else {

                    buffer[valread] = '\0'; 
                    for(j=0; j<max_clients; j++) {
                        int outSock = client_socket[j];
                        if(outSock != master_socket && outSock != sd) {
                            send(outSock , buffer , strlen(buffer) , 0 ); 
                        }
                    }

                }

            } 
        } 
    }

Client:

@FXML
void sendFile(ActionEvent event) {
    FileChooser fileChooser = new FileChooser();
    File file = fileChooser.showOpenDialog(null);

    // Send "start" message to let server know that I'm going to send a file
    out.println("start");
    out.flush();

    try {

        FileInputStream fis = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(fis);

        //Get socket's output stream
        OutputStream os = clientSocket.getOutputStream();

        //Read File Contents into contents array
        byte[] contents;
        long fileLength = file.length();
        long current = 0;

        while(current!=fileLength){
            int size = 10000;
            if(fileLength - current >= size)
                current += size;
            else{
                size = (int)(fileLength - current);
                current = fileLength;
            }
            contents = new byte[size];
            bis.read(contents, 0, size);
            os.write(contents);
            System.out.print("Sending file ... "+(current*100)/fileLength+"% complete!");
        }

        os.flush();
        System.out.println("File sent successfully!");

    } catch(Exception e) {

    }
}

public ChatWindowController() {

    try {
        clientSocket = new Socket("127.0.0.1", 54000);
        outToServer = new DataOutputStream(clientSocket.getOutputStream());
        inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        out = new PrintWriter(clientSocket.getOutputStream(), true);

        thread = new Thread() {
            @Override
            public void run() {
                try {

                    while(isRunning) {
                        if (ta_display != null) {
                            String message = inFromServer.readLine();
                            if (!isDownloadingFile) {
                                System.out.println(message);
                                ta_display.appendText(message + '\n');

                                if (message.equals("start")) {
                                    isDownloadingFile = true;
                                }
                            } else {

                                byte[] contents = new byte[10000];

                                //Initialize the FileOutputStream to the output file's full path.
                                FileOutputStream fos = new FileOutputStream("/example/test.png");
                                BufferedOutputStream bos = new BufferedOutputStream(fos);
                                InputStream is = clientSocket.getInputStream();

                                //No of bytes read in one read() call
                                int bytesRead = 0;

                                while((bytesRead=is.read(contents))!=-1)
                                    bos.write(contents, 0, bytesRead);

                                bos.flush();

                                System.out.println("File saved successfully!");

                            }
                        }

                    }

                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
        };

        thread.start();
    } catch(Exception e) {
        e.printStackTrace();
    }
}

When I click button sendFile method is called, then I choose file and I want to send this file, and with other clients download it.


Answer:

I have written a similar example for chatting at https://github.com/foreverpersist/socket/blob/master/chatroom.c.

File transferring is implemented by P2P

  1. Server tells <IP:PORT>s of sender and receiver to each other.
  2. Sender and receiver connect to each other directly.
  3. Sender sends file to receiver.
  4. Sender and receiver close the connection.

As what happens in your project, when transferring files with path Sender -> Server -> Receiver, the content of large files may be incomplete if you don't deal carefully. So, I just transfer files with path Sender -> Receiver.

Question:

I have a chat program. The problem is that I am trying to disallow dupe names. Essentially, whenever a name change request is sent to the server, it is checked against the list of names currently in use and if it is already taken, the person is added to my shitlist (not allowed to post) and they are sent a message that they need to change their name.

I commented the crap out of the code since there is a lot so you can understand it easily.

The problem is that the wrong person is being sent the message that the name is already in use! I have spent the last 8 hours trying to find it and It's bloody driving me mad!

The server side code is long; I'll post the relevant bits and any further will be provided on request. I'll also link to the complete program. (Not the source, the JAR.)

JAR: https://www.mediafire.com/?4t2shjdjf7blpg2

//...Irrelevant bits ommitted...//
public class Server
{
    // The server object reference
    static Server server;

    // Declarations:
    private ArrayList<ObjectOutputStream> clientOutputStreams; // out streams
    private ArrayList<String> takenNames = new ArrayList<>(); // taken names
    private InetAddress ip;
    private final int serverPort; // the port the server is running on
    private static ObjectOutputStream changer; // the last person to change names
    private ArrayList<ObjectOutputStream> shitList = new ArrayList<>();

    private HashMap <InetAddress, ObjectOutputStream> ipMap = 
            new HashMap<>(); // <ip, outputstream>

 //...Irrelevant bits ommited...//

// Don't mind this non-indentation, it is supposed to be.   
public void tellEveryone(Message message, InetAddress senderIP)
{
    // First check some special conditions..
    if(message.getType() == Message.TYPE.IN_USE)
    {
        try
        {
            changer.writeObject(message);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    // If someone is on my shitlist,
    if(shitList.contains(ipMap.get(senderIP)))
    {
        // Warn them of their sins...
        Message nopeMessage = new Message(Message.TYPE.SERVER, 
            "You may not send any messages until you change your name!", 
                "Server");
        try
        {
            ipMap.get(senderIP).writeObject(nopeMessage);
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }
    else
    {
        // Send message normally to everyone...
        // Sync, just to be safe
        synchronized(clientOutputStreams)
        {
            for(ObjectOutputStream oo : clientOutputStreams) // while more clients...
            {  
                try
                {
                    oo.writeObject(message);
                    oo.flush();
                }

                catch(IOException e)
                {
                    System.out.println("IOException caught during tellEveryone()");
                    e.printStackTrace();
                }
            }
        }
        System.out.println(getTimeStamp() + ": Message Sent by:".
                concat(" " + senderIP + "/ " + message.getSenderName()));
    }
}

The server handler inner class...

    public class ServerHandler implements Runnable
    {
        @Override
        public void run()
        {
            // Create a list of client out streams to send stuff...
            clientOutputStreams = new ArrayList<>();
            try // To establish a connection with clients
            {
                // Create server socket...
                ServerSocket serverSocket = new ServerSocket(serverPort);
                while(true) // Will always run! Blocks!
                {
                    // Assign a client socket to any new socket connections...
                    // (The var used here is temp, but will be passed off soon.)
                    Socket clientSocket = serverSocket.accept();
                    // Get's the ip of the client that connected...
                    ip = clientSocket.getInetAddress();
                    System.out.println(ip + " " + "connected.");
                    // Create ooStream to send messages to client...
                    ObjectOutputStream ooStream = 
                            new ObjectOutputStream(
                                    clientSocket.getOutputStream());
                    // Add the client oo stream to the list of outputs...
                    clientOutputStreams.add(ooStream);
                    // Add user IP data to map of ip's
                    ipMap.putIfAbsent(ip, ooStream);
                    // Create new thread to run inner class ClientHandler...
                    Thread t = new Thread(new ClientHandler(clientSocket));
                    // Running the thread makes it safe to overwrite the... 
                    // ...clientsocket variable.
                    t.start();
                }
            }
            catch (IOException e)
            {
                System.out.println("Exception in server.run()");
                // TODO: Revise
                e.printStackTrace();
            }
        }
    }

The client handler inner class

public class ClientHandler implements Runnable
{
    private ObjectInputStream oInStream; // The client's input stream.
    private Socket socket; // Socket to the client

    public ClientHandler(Socket clientSocket)
    {
        try // to create an input stream...
        {
            socket = clientSocket; // <-- The one passed in to the method
            // Potential error from previous version... REMOVE WHEN TESTED
            oInStream = new ObjectInputStream(socket.getInputStream());
        }
        catch(IOException e)
        {
            System.out.println("Error establishing input stream");
        }
    }

    @Override
    public void run()
    {
        Message message;

        try // To process incoming messages...
        {
            while(socket.isClosed() == false) // If the socket is open...
            {
                // While there are more messages...
                // Also assigns to the message var.
                while((message = (Message)oInStream.readObject()) != null)
                {
                    // Passes on the message and sender info.
                    if(message.getType() == Message.TYPE.NAME_REQUEST)
                    {
                        changer = ipMap.get(socket.getInetAddress());
                        System.out.println(socket.getInetAddress());
                        System.out.println(changer.toString());
                        handleNameRequests(message);
                    }
                    else
                    {
                        tellEveryone(message, ip); // TEST CHANGE- DELETED IF TEST
                    }
                }
                // TEST TEST TEST
                synchronized(clientOutputStreams)
                {
                    int index = 
                            clientOutputStreams.indexOf(
                                    socket.getOutputStream());
                    clientOutputStreams.remove(index);
                    System.out.println("Removed the client in sync");
                }
            }
            // TEST TEST TEST
            socket.close(); // TEST CLOSING SOCKET WHEN DONE.
            System.out.println("Sock closed after while loop in ch run()");
        }
        catch(IOException e)
        {
            System.out.println("IOException caught when "
                    + "reading message.");
        }
        catch (ClassNotFoundException e)
        {
            System.out.println("Some poor sap is going to have to debug"
                    + "this!");
        }
        finally
        {
            // THIS WHOLE BLOCK: TEST TEST TEST
            try
            {
                oInStream.close();
                System.out.println("just closed oinStream");
            }
            catch(IOException e)
            {
                e.printStackTrace();
            }
        }
    }
}

Answer:

I FINALLY FOUND IT!

For any future people encountering a similar problem, the problem was that I was assigning the ip variable in the wrong place! This essentially resulted in the list of ip's being all the same! Another bug confounded that problem, in that when I disabled the sending ability of messages when on my shitlist (aren't programmers the darndest?), I disabled ALL types of messages, including those from the server, ect! Oops!

The lesson? Bugs hide in the darndest places. Walk through everything, and doubt what you know to be true. Assume nothing, verify everything. There are never enough print statements when debugging!

Question:

I have problem with my ClientListener, server send TCP package but Client do not display it.

Here is my code

https://github.com/Behoston/sieci/tree/master/src/tcp

(Server run new thread for each new client and this thread (to listen) and this thread run other to sending messages (it works - I can see messages in wireshark Client run main thread where can input some text and second therad (not working properly) to listen messages from serever)


Answer:

It's quite hard to answer your question if you only provide a link to your repository, some code snippets of the crucial classes or methods would be helpful.

If my understanding of the classes you provided is correct, this is the workflow you are trying to implement (correct me if I'm wrong):

  1. Server gets started
  2. Client connects
  3. Server sends a welcome message (which is not getting displayed on clientside):

    DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
        outToClient.writeBytes("Hello, your ID is: " + clientId + "; To send message please use 3 first chars to set target");
    

On the recieving side:

    BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            response = inFromServer.readLine();

As far as I see, you are waiting for a readLine() with your server sending a byteArray. Since readLine() waits for a line seperator (f.e. "\n"), your messages can't be processed.