Daniel Holubář
Daniel Holubář

Reputation: 55

C server returns only first message

I have a simple C server and simple Java client. Client takes input from keyboard and sends it to server. Server prints it and sends it back. But it works only for the first time, what can be the problem?

Main:

package test;

import java.util.Scanner;

public class Main {


    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Client cli = new Client();
        cli.connect();

        while(sc.hasNextLine()) {
            cli.send(sc.nextLine());
            System.out.println(cli.rec());
        }

    }
}

Client:

package test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {

    Socket socket;
    OutputStreamWriter out;
    BufferedReader in;

    public void connect() {

        try {
            socket = new Socket("localhost", 50001);
            socket.setSoTimeout(5000);
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        try {
            out = new OutputStreamWriter(socket.getOutputStream());
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        try {
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void send(String message) {
        try {
            this.out.write(message + "\n");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            this.out.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public String rec() {
        try {
            return in.readLine();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "";
    }
}

Server:

#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>

#define MY_PORT     50001
#define MAXBUF      1024

int main(int Count, char *Strings[])
{   int sockfd;
    struct sockaddr_in self;
    char buffer[MAXBUF];
    int clientfd;
    struct sockaddr_in client_addr;
    int addrlen=sizeof(client_addr);
    int yes = 1;

    /*---Create streaming socket---*/
    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
    {
        perror("Socket");
        exit(errno);
    }

    /*---Initialize address/port structure---*/
    bzero(&self, sizeof(self));
    self.sin_family = AF_INET;
    self.sin_port = htons(MY_PORT);
    self.sin_addr.s_addr = INADDR_ANY;

    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == 0)
        {
            printf("Socket options was set.\n"); //TODO
        }
        else
        {
            printf("Set socket options failure.\n"); //TODO
        }

    /*---Assign a port number to the socket---*/
    if ( bind(sockfd, (struct sockaddr*)&self, sizeof(self)) != 0 )
    {
        perror("socket--bind");
        exit(errno);
    }

    /*---Make it a "listening socket"---*/
    if ( listen(sockfd, 20) != 0 )
    {
        perror("socket--listen");
        exit(errno);
    }

    clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &addrlen);

    /*---Forever... ---*/
    while (1)
    {
        memset(buffer, 0, MAXBUF);
        /*---accept a connection (creating a data pipe)---*/
        printf("%s:%d connected\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

        recv(clientfd, buffer, MAXBUF, 0);
        printf("Server got: %s", buffer);
        //sprintf(buffer, "%s\r\n", buffer);
        /*---Echo back anything sent---*/
        printf("sending...\n");
        int n = send(clientfd, buffer, MAXBUF, 0);
        printf("sent: %d\n", n);
    }

    /*---Close data connection---*/
            close(clientfd);

    /*---Clean up (should never get here!)---*/
    close(sockfd);
    return 0;
}

Upvotes: 1

Views: 506

Answers (3)

chris01
chris01

Reputation: 12331

The accept must be inside your endless-loop. It accepts any new connection there. So you only accept the 1st one and no other.

listen is the last call you do only once. Then you do it per connection. Thats also the way if you would have some scalability (e.g. fork at accept).

Upvotes: 0

Kishore Bandi
Kishore Bandi

Reputation: 5711

Well, here goes the root cause. (Skip to fix if you don't want the details)

  1. When you were sending data back to Java Client, you were sending the length of the data as MAXBUF (1024) in your case.
  2. Now, What apparently happened is java received 1024 characters as the input. You can see this in your (C-language) Server output. sent: 1024
    To fill for this remaining characters, it sends the ASCII value 0 (which is NUL)
    You can verify this by removing your readLine() API in your java code and adding this.

        StringBuilder strb = new StringBuilder();
        while ((value = inputStream.read()) != -1) {
            if(value != 0)
                strb.append((char) value);
            System.out.print(value + ":" + (char) value+",");
        }
        return strb.toString();
    
  3. The first time request comes from server back to client with user-entered-data followed by remaining nulls, the following happens

    • Since readLine() API was used. It reads till \n or \r character. So you're able to see the output.
    • At this point bufferedReader still has those NUL characters stored in its buffer.
    • Now when second time we try to read from this reader, it first tries to empty the old buffer until (No \n or \r was found, except all NULs). It appends all these to the StringBuilder (which it internally uses) followed by which it appends the newly read string to the builder.
      If This NUL character occurs, then all characters appended to the StringBuilder disappears in its buffer. (Explanation given here) You can try this with this code:

      StringBuilder str = new StringBuilder().append((char)0).append("kishore");
      System.out.println(str.toString()); // Output : Blank
      
    • That's the reason you were not able to see the output. So you client and server communication had no issue.

  4. Fix: Change your "C" Server code to send the proper length.

        int bytes = recv(clientfd, buffer, MAXBUF, 0);
        ...
        int n = send(clientfd, buffer, bytes, 0);
    

With this it should work.

Upvotes: 3

Ofer Arial
Ofer Arial

Reputation: 1139

I'm not sure that this will solve your problem, but I think that your accept function call should go in the while loop, as I saw that Jacajack just commented.

This way, the loop will accure each time you will recieve a connection. It should look like this:

Example:

/* Do what's in the loop every time that you accept a connection */

while (clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &addrlen))
{
    memset(buffer, 0, MAXBUF);
    /*---accept a connection (creating a data pipe)---*/
    printf("%s:%d connected\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

    int iResult = recv(clientfd, buffer, MAXBUF, 0); 
    // iResult is the amount of data from recv

    // Handle information and echo it back 


}

Additionally, I think you should implement some kind of protocol that will validate the message that the server got (see that it really does ends with a end of line or any other validitation that you would like to specify).

Upvotes: 1

Related Questions