1

我有一个奇怪的问题。我正在尝试编写 C 客户端和 Java 服务器之间的接口。为此,我用 Java 编写了一个网关(它使用 RMI 与服务器通信)。几乎一切正常,但我试图从网关返回一些整数到 C 客户端。这是代码:

Java网关:

import java.net.Socket;
import java.net.ServerSocket;
import java.net.MalformedURLException;
import java.io.BufferedReader;
import java.net.InetSocketAddress;
import java.io.InputStreamReader;
import java.io.DataOutputStream;
import java.lang.System;

public class hotelgw{

  public static final int PORT = 4242;
  public static final int BACKLOG = 5;
  public static final int MAX_ARGS = 5;

  public hotelgw(){
    InetSocketAddress address;
    ServerSocket socket = null;
    try{
      address = new InetSocketAddress(PORT);
      socket = new ServerSocket(PORT,BACKLOG);
    }catch (Exception e){
      System.out.println("Error: "+e);
      System.exit(1);
    }

    while(true){

      Socket newsock = null;
      try{
       newsock =  socket.accept();
      }catch (Exception e){
        System.out.println("Error: "+e);
        System.exit(1);
      }

      BufferedReader in = null;
      DataOutputStream out = null;

      try{
        in = new BufferedReader(new InputStreamReader(newsock.getInputStream()));
        out = new DataOutputStream(newsock.getOutputStream());
      }catch (Exception e){
        System.out.println("Error: "+e);
        System.exit(1);
      }


      String[] init_args = new String[MAX_ARGS];
      int i = 0;
      String c;

      try{
        while(!(c = in.readLine()).equals("end")){
          System.out.printf("%s",c);
          init_args[i] = c;
          i++;
        }
      }catch (Exception e){
        System.out.println("Error: "+e);
        System.exit(1);
      }

      for(int j=0;j<init_args.length;j++){
        System.out.printf("init_args[%d] = %s\n", j, init_args[j]);
      }

      int counter = 0;
      for(int j=0;j<init_args.length;j++){
        if(init_args[j] != null){
          counter++;
        }
      }

      String[] final_args = new String[counter];
      for(int j=0;j<counter;j++){
        final_args[j] = init_args[j];
      }

      if(final_args[1].equals("list")){
        int[] list = hotelclient.get_list(final_args);
        System.out.println("list received in hotelgw.java");
        for(int j=0;j<list.length;j++){
          try{
            out.writeInt(list[j]);
          }catch (Exception e){
            System.out.println("Error writing to socket: " + e);
            System.exit(1);
          }
        }
      }


      try{
        in.close();
        newsock.close();
      }catch (Exception e){
        System.out.println("Error: "+e);
        System.exit(1);
      }
    }
  }
}

C客户端:

#include <stdio.h>
#include <sys/socket.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define PORT 4242
#define TYPES_OF_ROOMS 3
#define BUFFER_SIZE 64

/*Writes message to socket*/
ssize_t writen(int fd, const void *vptr, size_t n)
{
  size_t nleft;
  ssize_t nwritten;
  const int *ptr;

  ptr = vptr;
  nleft = n;
  while(nleft>0){
    if ( ((nwritten = write(fd,ptr,nleft)) <= 0)){
      if (errno == EINTR)
        nwritten = 0;
      else
        return -1;

    }
    nleft -= nwritten;
    ptr += nwritten;
  }
  return n;
}


int create_socket(char* address){
  struct hostent *server_address;
  struct in_addr *addr;
  struct sockaddr_in server_addr;
  socklen_t addrlen;
  char* ip;

  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if(sockfd<0){
    perror("Error creating socket");
    exit(1);
  }

  server_address = gethostbyname(address);
  if(server_address == NULL){
    fprintf(stderr, "Server not found!\n");
    exit(1);
  }else{
    addr = (struct in_addr*) server_address->h_addr_list[0];
  }

  ip = inet_ntoa(*addr);

  server_addr.sin_family  =  AF_INET;
  server_addr.sin_port    =  htons(PORT);
  server_addr.sin_addr.s_addr  =  inet_addr(ip); 

  addrlen = (socklen_t) sizeof(struct sockaddr_in);

  if(connect(sockfd,(struct sockaddr *) &server_addr, addrlen)){
    perror("Error connecting to server");
    exit(1);
  }

  return sockfd;
}


int main(int argc, char** argv){
  int sockfd, err, i, type1=0, type2=0, type3=0;
  int int_buf[TYPES_OF_ROOMS];
  char string_buf[BUFFER_SIZE];
  char newline = '\n';
  char *end = "end";

  if(argc == 1){
    printf("Usage: hotelgwclient <address> {list,guests,book} [room type] [guest name]\n");
    exit(1);
  }

  sockfd = create_socket(argv[1]);
  printf("Socket created\n");


  if(strcmp(argv[2], "list")==0){
    if(argc != 3){
      printf("Usage: hotelgwclient <address> list\n");
      exit(1);
    }


    printf("list initiated\n");
    writen(sockfd, argv[1], strlen(argv[1]));
    writen(sockfd, &newline , sizeof(newline));
    writen(sockfd, argv[2], strlen(argv[2])); 
    writen(sockfd, &newline, sizeof(newline));
    writen(sockfd, end, strlen(end));
    writen(sockfd, &newline, sizeof(newline));
    printf("Written to socket\n");

    err = read(sockfd, &type1, sizeof(int));
    err = read(sockfd, &type2, sizeof(int));
    err = read(sockfd, &type3, sizeof(int));

    printf("Read from socket\n");
    if(err<0){
      perror("Error reading from socket");
      exit(1);
    }
    /*
    for(i=0;i<TYPES_OF_ROOMS;i++){
      printf("%d\t",int_buf[i]);
    }
    */
    type1 = htonl(type1);
    type2 = htonl(type2);
    type3 = htonl(type3);

    printf("%d\t",type1);
    printf("%d\t",type2);
    printf("%d\t",type3);
    printf("\n");
    return 0;
  }

  printf("Command not recognized.\nUsage: hotelgwclient <address> {list,guests,book} [room type] [guest name]\n");
  exit(1);
}

(注意,我知道它很乱,我会在它工作时清理它:))。

问题是,每当我调用 list 方法时,C 尝试读取响应(三个整数)它只会读取一个字节,而不是四个。如果我在从套接字读取之前添加 sleep() 调用,则此问题已修复。有谁知道发生了什么以及我应该做些什么来解决它?!

谢谢!

4

3 回答 3

2

我怀疑问题是您假设当最小值始终为 1(即使在 Java 中)时,您将读取尽可能多的数据

为什么您在其他字节之前获得一个字节是因为您使用的是没有 BufferedOutputStream 的 DataOutputStream ,因此它一次发送一个字节。使用此解决方法可能很诱人,但它只是隐藏了您需要能够一次正确读取一个字节的潜在问题,因为这总是有可能的。

于 2012-10-19T12:29:00.447 回答
1

它对read返回您请求的更少字节有效。从它的手册页

返回值
成功时,返回读取的字节数... 如果这个数字小于请求的字节数,则不是错误;这可能会发生

您需要read循环调用,直到返回预期的字节数或错误:

unsigned char* p = &type1;
int expected = sizeof(int);
while (expected > 0) {
    err = read(sockfd, p, expected);
    if (err < 0) {
        break;
    }
    p += err;
    expected -= err;
}
于 2012-10-19T12:28:33.923 回答
0

当涉及阻塞调用并且必须并行执行时,您必须为每个任务添加线程(例如一个用于读取,另一个用于写入),以便它们不会阻塞执行。

还将收到的所有内容放入缓冲区并尝试从那里解析。使用 pthread api 实现线程。

于 2012-10-19T12:27:28.127 回答