2016-08-03 155 views
1

我试图实现客户端与客户端之间的通信,并与它们之间的服务器通信。服务器的功能是,当客户端假设客户端A向服务器发送消息时,服务器应该将该消息转发给其他客户端客户端B.当客户端B向应该转发的消息发送消息时到客户端A.这个程序只涉及两个客户端。 当我执行的代码我得到的错误是,它说:使用select()函数进行客户端与客户端通信c

Socket Operation on Non-socket 

我得到这个错误时,从客户端A接收到的消息被转发到客户端B.我认为这个问题是由于收到的存储客户B的地址到客户A的地址。我不确定。

到目前为止我的服务器代码。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <sys/types.h> 
#include <arpa/inet.h> 
#include <signal.h> 
#include <unistd.h> 
#include <errno.h> 
#include <sys/time.h> 
#define SERVER_PORT 5009 


int main(){ 
unsigned int sockfd, c,c1,c2, clientlen, clientfd; 
struct sockaddr_in server; 
struct sockaddr_in client1; 
int clientsocks[2]; 
char rmsg1[100], msg1[100],rmsg2[100], msg2[100]; 
char w_msg[] = "Connection to server established"; 

fd_set readfds; // For temp file descriptor list. 

clientsocks[0] = 0 ; 
clientsocks[1] = 0 ; 
//Socket Creation Process. 
sockfd = socket(AF_INET, SOCK_STREAM, 0); 
if(sockfd < 0){ 
    perror("Socket cannot be created"); 
} 

//For reusing the socket. 
int on = 1; 
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 
//Socket address 
bzero((char *) &server, sizeof(server)); 
server.sin_family = AF_INET; // IPv4 internet Protocols 
inet_aton("127.0.0.1", &server.sin_addr); 
server.sin_port = htons(SERVER_PORT); 

//Binding socket to address. 
if (bind(sockfd, (struct sockaddr*)&server, sizeof (server)) < 0){ 
    perror("Bind Error"); 
    exit(EXIT_FAILURE); 
} 

//Listen to accept connection. 
if(listen(sockfd, SOMAXCONN) < 0){ 
    perror("Error in Listen"); 
    exit(EXIT_FAILURE); 
} 

unsigned int new_sock; 
clientlen =sizeof(client1); 
int activity; 
while(1){ 

    //Clear socket set. 
    FD_ZERO(&readfds); 

    //Adding main sockfd to the socket set. 
    FD_SET(sockfd, &readfds); 
    unsigned int max_sd = sockfd; 

    //Add child sockets to set. 
    for(int i=0 ; i<2; i++){ 
      c = clientsocks[i]; 
     if(c > 0) 
      FD_SET(c, &readfds); 
     if(c > max_sd) 
      max_sd = c; 
    } 

    activity = select(max_sd + 1, &readfds, NULL, NULL, NULL); 
    if(activity < 0){ 
     perror("Error in select()"); 
     exit(EXIT_FAILURE); 
    } 

    //Incoming connection when something happens on sockfd. 
    if(FD_ISSET(sockfd, &readfds)){ 
     new_sock = accept(sockfd, (struct sockaddr *) &client1, &clientlen); 
     if(new_sock > 0){ 
      for(int i=0; i<2; i++){ 
       if(clientsocks[i] == 0){ 
        clientsocks[i] = new_sock; 
        break; 
       } 
      } 
     } 
     if( new_sock < 0){ 
      perror("Error Accepting"); 
      exit(EXIT_FAILURE); 
     } 
     if(send(new_sock, w_msg, strlen(w_msg), 0) != strlen(w_msg)){ 
      perror("Welcome message"); 
      exit(EXIT_FAILURE); 
     } 
    c1 = clientsocks[0]; 
    c2 = clientsocks[1]; 
    FD_SET(c1, &readfds); 
    FD_SET(c2, &readfds); 
    } 

    //Else if its not a new incoming connection. 
    if(FD_ISSET(c1, &readfds)){ 
     if(recv(c1, rmsg1, 100, 0) < 0){ 
      perror("Receive 1"); 
      exit(EXIT_FAILURE); 
     } 
     printf("Client1 >> %s\n", rmsg1); 
     //Forwarding to Client B. 
     if(send(c2, rmsg1, 100, 0) < 0){ 
      perror("Error forwarding to 2"); 
      exit(EXIT_FAILURE); 
     } 
    } 
    if(FD_ISSET(c2, &readfds)){ 
     if(recv(c2, rmsg2, 100, 0) < 0){ 
      perror("Receive 2"); 
      exit(EXIT_FAILURE); 
     } 
     printf("Client2 >> %s\n", rmsg2); 
     if(send(c1, rmsg2, 100, 0) < 0){ 
      perror("Error Forwarding to 1"); 
      exit(EXIT_FAILURE); 
     } 
    } 
} 
close(sockfd); 
return 1; 
} 

我的问题只涉及两个客户端。如果你能指出一些其他的改进,我会很感激。

+2

如果出现错误,让执行继续进行就像它没有发生一样是不可接受的。这些'perror()'调用中的每一个都应该跟随一个清理和返回。 – EJP

+1

客户端套接字如何被存储到“clientsocks”中? – immibis

+0

@EJP你的意思是我应该打破循环,并返回? –

回答

1

你有一些结构性问题,并不是真正需要StackOverflow成员才能解决的问题。

  1. 决定每个客户端是否要连接到相同的套接字或它们自己的唯一套接字上,相应地进行监听。
  2. 如果你有一个共享连接,它似乎你要... ...并通过共享连接我的意思是你正在监听一个端口和多个接受,然后在给定的接受后,你不能假设谁已连接直到你从港口读取一些数据。你应该有每个客户端通过发送一些信息,让你知道,如果一个连接,然后B或当B连接,然后A.
  3. 在你的setsockopt的代码好像你忘{}
  4. 在保存clientsocks你可以使用来自客户端的信息来确定使用哪个阵列插槽。也许你有100个客户端,他们每个都连接,然后发送一个32位字(包含他们的客户端号码,在1-100之间)。
  5. 一旦你可以阅读来自不同客户端的消息(可能希望现在打印出来,以便看到发生了什么),你可以构建各种消息。目标是客户端A能够向服务器询问客户端B的联系信息,以便A可以直接连接到B并发送消息B。
  6. 或者,代替#5,客户端26应该能够向服务器发送消息,指示其用于客户端45,并且服务器应该能够检查已经签入的客户端的数组,然后发送消息到阵列插槽45中的客户端。
  7. 客户端需要具有唯一的ID /号码,以便服务器可以将客户端ID映射到clientarray套接字。
  8. 其中一些错误直接处理比直接导致服务器退出。也许你可以关闭与导致错误的套接字索引关联的套接字。您的错误消息应指出哪个客户端/套接字索引导致错误。一般来说,你需要更多的调试信息和更多的信息。
+0

对不起,延迟回复。但它是一个很棒的答案。下次我会问一些问题时会照顾这些事情。 –