2013-02-24 80 views
3

對於網絡編程我很新穎。我有一個UDP客戶端/服務器,以大寫或小寫形式向服務器發送消息。服務器收到該消息並將其中繼交換機箱。我無法弄清楚我將如何將其轉發回第一個客戶端,並將其發送給client2。繼承我的代碼。C:2客戶端和1服務器中的UDP套接字編程

服務器:

/* 
Simple udp server 

*/ 
#include<stdio.h> //printf 
#include<string.h> //memset 
#include<stdlib.h> //exit(0); 
#include<arpa/inet.h> 
#include<sys/socket.h> 
#include<ctype.h> 

#define BUFLEN 512 //Max length of buffer 
#define PORT 8888 //The port on which to listen for incoming data 

void die(char *s) 
{ 
    perror(s); 
exit(1); 
} 

int main(void) 
{ 
struct sockaddr_in si_me, si_other, si_other2; 

int s, i, slen = sizeof(si_other) , recv_len; 
char buf[BUFLEN]; 

//create a UDP socket 
if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) 
{ 
    die("socket"); 
} 

// zero out the structure 
memset((char *) &si_me, 0, sizeof(si_me)); 

si_me.sin_family = AF_INET; 
si_me.sin_port = htons(PORT); 
si_me.sin_addr.s_addr = htonl(INADDR_ANY); 

//bind socket to port 
if(bind(s , (struct sockaddr*)&si_me, sizeof(si_me)) == -1) 
{ 
    die("bind"); 
} 

//keep listening for data 
while(1) 
{ 
    printf("Waiting for data..."); 
    fflush(stdout); 

    //try to receive some data, this is a blocking call 
    if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen)) == -1) // read datagram from server socket 
    { 
     die("recvfrom()"); 
    } 

    //print details of the client/peer and the data received 
    printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));   printf("Data: %s\n" , buf); 

    //now reply to server socket/the client with the same data 
    if (sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) == -1) 
    { 
     die("sendto()"); 
    } 



} 

close(s); 
return 0; 
} 

客戶:

/* 
Simple udp client 

*/ 
#include<stdio.h> //printf 
#include<string.h> //memset 
#include<stdlib.h> //exit(0); 
#include<arpa/inet.h> 
#include<sys/socket.h> 
#include<ctype.h> 

#define SERVER "192.x.x.x" 
#define BUFLEN 512 //Max length of buffer 
#define PORT 8888 //The port on which to send data 

void die(char *s) 
{ 
perror(s); 
exit(1); 
} 

int main(void) 
{ 
struct sockaddr_in si_other; 
int s, s2, i, slen=sizeof(si_other); 
char buf[BUFLEN]; 
char message[BUFLEN]; 

if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)  // create a client socket 
{ 
    die("socket"); 
} 

memset((char *) &si_other, 0, sizeof(si_other)); 
si_other.sin_family = AF_INET; 
si_other.sin_port = htons(PORT); 

if (inet_aton(SERVER , &si_other.sin_addr) == 0)   // Create datagram with server IP and port. 
{ 
    fprintf(stderr, "inet_aton() failed\n"); 
    exit(1); 
} 

while(1) 
{ 
    printf("Enter message : "); 
    gets(message); 


    int a; 
    char message2[BUFLEN]; 
    for(a=0;a<=BUFLEN-1;a++) 
     { 
     if(message[a] >= 97 && message[a] <= 122) 
      message2[a] = toupper(message[a]); 
     else 
      message2[a] = tolower(message[a]); 

     } 


    if (sendto(s, message2, strlen(message2) , 0 , (struct sockaddr *) &si_other, slen)==-1) 
    { 
     die("sendto()"); 
    } 


    //receive a reply and print it 
    //clear the buffer by filling null, it might have previously received data 
    memset(buf,'\0', BUFLEN); 
    //try to receive some data, this is a blocking call 
    if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == -1)  // read datagram from client socket 
    { 
     die("recvfrom()"); 
    } 

    puts(buf); 
} 

close(s); 
return 0; 
} 
+1

由於UDP不是面向連接的,所以實際上並沒有客戶端。您必須等到客戶端與服務器通信,然後跟蹤客戶端。 – 2013-02-24 15:14:54

+1

UDP沒有客戶端。因爲它是無連接的,所以服務器上的你要跟蹤你的客戶和發送給誰。 – Linuxios 2013-02-24 15:15:53

+1

您的代碼將回復發回給它已收到數據的相同'si_other'。嘗試先讓兩個客戶端'si_other',然後你可以區分。 – hiteshradia 2013-02-24 15:23:42

回答

6

因爲這有沒有明確的答案21K的意見,這是編碼UDP的基本的瞭解。我一定要給它點愛。

正如已經在評論中提到:在您的服務器代碼,從客戶端使用收到一條消息:

recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen)) 

此功能的結果是該消息的數據將被寫入buf和IP發送消息的套接字的地址和端口號將被填充到si_other(它必須是struct sockaddr_in)。

那麼,當您使用發送響應:

sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) 

由於si_other,它要傳遞的目的地址sendto,包含你拿到了最後消息的IP /端口,響應將總是回到你收到的最後一封郵件的發件人。在許多服務器應用程序中,這是一種非常常見的情況:您從某處收到請求,您會將回復發送回相同的地方。

如果你想讓消息去別的地方,除了發送你請求的進程外,你需要創建一個不同的struct sockaddr_in變量,它包含你希望消息去的地址和端口。

而在你的客戶端代碼,您已經有這樣做,例如,(清理位)代碼:

struct sockaddr_in si_client2; 

memset((char *) &si_client2, 0, sizeof(si_client2)); 
si_client2.sin_family = AF_INET; 
si_client2.sin_port = htons(CLIENT2_PORT); 
if(inet_aton(CLIENT2_HOST, &si_client2.sin_addr) == 0) 
    perror("inet_aton"); 

所以,現在,如果你在你的sendto(使用si_client2),數據包將去那個客戶。

因爲它是UDP,所以不能保證交付。如果有一個進程在該IP地址處偵聽UDP,則在該端口號上,然後(如果沒有發生網絡錯誤),它將獲得該消息。如果不是的話,什麼都不會發生。你的信息將消失在虛空中。請注意,如果客戶端1和客戶端2都在同一臺計算機上運行,​​則它們將需要使用不同的端口號,因爲每個目標(無論是在客戶端還是服務器角色中)都必須具有IP的唯一組合和PORT。

現在,在現實生活中,服務器很少會提前知道客戶端的IP和端口。通常客戶端不會使用固定端口號,而是使用「臨時端口」端口操作系統在運行時分配的數字。而客戶端通常會配置服務器的IP和端口。

因此,在大多數情況下,您將在服務器中保留一些客戶端地址列表中的代碼。也許一個簡單的消息服務會保留最近100個獲得消息的客戶端列表......但實際上如何實現將取決於應用程序的需求。對於這樣一個簡單的練習,你可以像我剛纔所說的那樣對地址進行硬編碼...

底線是,要將UDP數據包發送到特定的目的地,發件人必須知道IP和PORT該特定目的地。而要知道這一點的唯一方法是要麼有一些配置數據,要麼有人(如目的地)提前發送數據包,讓你知道它的存在。請記住,使用UDP套接字,您可以從任何地方獲得消息,並且您將獲得IP /端口以及消息。如果你需要發送消息,你需要知道你想發送它的IP /端口。這是您的應用程序問題,要弄清楚它將如何獲取該信息以及將信息存儲在何處以供以後使用。