C服务器仅返回第一条消息

时间:2017-01-11 20:34:25

标签: java c sockets

我有一个简单的C服务器和简单的Java客户端。客户端从键盘获取输入并将其发送到服务器。服务器打印并将其发回。但它只是第一次有效,可能是什么问题?

主:

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());
        }

    }
}

客户端:

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 "";
    }
}

服务器:

#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;
}

3 个答案:

答案 0 :(得分:3)

嗯,这里是根本原因。 (如果您不想要详细信息,请跳过修复)

  1. 当您将数据发送回Java客户端时,您发送的数据长度为MAXBUF(1024)。
  2. 现在,显然发生的是java收到1024个字符作为输入。您可以在(C语言)服务器输出中看到这一点。 sent: 1024
    要填写剩余的字符,它会发送ASCII值0 (这是NUL)
    您可以通过删除java代码中的readLine()API并添加它来验证这一点。

        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. 第一次请求来自服务器返回客户端,用户输入的数据后跟剩余的空值,发生以下情况

    • 由于使用了readLine()API。它会读到\n\r个字符。所以你能看到输出。
    • 此时bufferedReader仍然将这些NUL字符存储在其缓冲区中。
    • 现在,当我们第二次尝试从此阅读器中读取时,它首先尝试清空旧缓冲区,直到找到(No \n\r,除了所有NUL)。它将所有这些附加到StringBuilder(它在内部使用),然后将新读取的字符串附加到构建器。
      如果出现此NUL字符,则附加到StringBuilder的所有字符都会在其缓冲区中消失。 (Explanation given here)您可以使用以下代码尝试此操作:

      StringBuilder str = new StringBuilder().append((char)0).append("kishore");
      System.out.println(str.toString()); // Output : Blank
      
    • 这就是你无法看到输出的原因。因此,客户端和服务器通信没有问题。

  4. 修复:更改“C”服务器代码以发送正确的长度。

        int bytes = recv(clientfd, buffer, MAXBUF, 0);
        ...
        int n = send(clientfd, buffer, bytes, 0);
    
  5. 有了它,它应该有用。

答案 1 :(得分:1)

我不确定这会解决您的问题,但我认为您的accept函数调用应该在while循环中,因为我看到Jacajack刚评论过。

这样,每次收到连接时,循环都会生效。它应该是这样的:

示例

/* 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 


}

此外,我认为您应该实现某种协议,验证服务器获得的消息(请参阅它确实以行结束或您希望的任何其他验证结束)指定)。

答案 2 :(得分:0)

接受必须在你的无限循环中。 它接受那里的任何新连接。所以你只接受第一个而不是其他。

听是您只做一次的最后一次通话。然后你按连接做。如果你有一些可扩展性(例如fork at accept),这也是你的方式。