2016-08-03 106 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

對不起,延遲迴復。但它是一個很棒的答案。下次我會問一些問題時會照顧這些事情。 –

相關問題