30/09/2018, 18:16

Cách xây dựng server_relay.c và client.c để nhắn tin như Yahoo chat bằng C?

Mình cần viết hai file là client.cserver_relay.c hay client.javaserver_relay.java có nghĩa là như vầy:

Tin nhắn từ client(1) gửi đến server_relay (hiện tin nhắn) và truyền đến client(2) và ngược lại như sau

client (1) <==> server_relay <==> client (2) 

(chỉ có 1 file client.c)

client(1) > hi 
server_relay :>hi
client (2): < hi

---

client(2) : < hi
            : > hello 
server_relay : > hi
                   : < hello
client (1): < hi
              : < hello

cơ bản là nó hoạt động như thế

Khi mình chạy file client.cserver_relay.c trên terminal của linux, tin nhắn của client (1) đi qua và hiện lên trên server_relay và hiện trên client (2) và ngược lại (giống như yahoo chat vậy)

Dựa vào cái đề mình đã viết được file proxy nhưng mình cần file này vùa có chức năng server và proxy để tạo thành file server_relay

Mình để file C mình đã viết và đề bài trong file zip bạn nào coi hộ mình với và giúp mình với, đây là link file của mình http://ge.tt/9RNrtNO2/v/0?c3

Code hiện có:

client.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;

    char buffer[256];
    if (argc < 3) {
       fprintf(stderr,"usage %s hostname port
", argv[0]);
       exit(0);
    }
    portno = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");
    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host
");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
        error("ERROR connecting");
    printf("Please enter the message: ");
    bzero(buffer,256);
    fgets(buffer,255,stdin);
    n = write(sockfd,buffer,strlen(buffer));
    if (n < 0) 
         error("ERROR writing to socket");
    bzero(buffer,256);
    n = read(sockfd,buffer,255);
    if (n < 0) 
         error("ERROR reading from socket");
    printf("%s
",buffer);
    close(sockfd);
    return 0;
}

##proxy.c

/* A simple server in the internet domain using TCP
   The port number is passed as an argument */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 

//error function as uses lots
void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{	
	//declarations
    int sockfd, newsockfd, sendsockfd, serv_portno, cli_portno, n;
    struct sockaddr_in serv_addr, host_addr, cli_addr;
    socklen_t clilen;
    struct hostent *host;
    char buffer[65556];
    if (argc < 4) {
       fprintf(stderr,"usage %s hostname serverport hostport
", argv[0]);
       exit(0);
    }
   
	serv_portno = atoi(argv[3]);
	cli_portno = atoi(argv[2]);
	//creating socket for server to accept connections
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0) error("ERROR opening socket");
	//creating socket for client component to send on
	sendsockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sendsockfd < 0) error("ERROR opening socket");
   
	//setting up the server component of the proxy
     bzero((char *) &serv_addr, sizeof(serv_addr));
     serv_addr.sin_family = AF_INET; //set address family for address (must be IPV4 as defined by AF_INET)
     serv_addr.sin_addr.s_addr = INADDR_ANY; //gets ip address of server
     serv_addr.sin_port = htons(serv_portno); //assign port number after ensuring it is big-endian
	 
	 //set up client part of proxy
 host = gethostbyname(argv[1]);
    if (host == NULL) {
        fprintf(stderr,"ERROR, no such host
");
        exit(0);
    }
    bzero((char *) &host_addr, sizeof(host_addr));
    host_addr.sin_family = AF_INET;
    bcopy((char *)host->h_addr, (char *)&host_addr.sin_addr.s_addr,host->h_length);
    host_addr.sin_port = htons(cli_portno);

    if (connect(sendsockfd,(struct sockaddr *) &host_addr,sizeof(host_addr)) < 0) 
        error("ERROR connecting");

     //bind socket to defined address and port, throw error if binding fails.
     if (bind(sockfd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) error("ERROR on binding");
	
	//listen for connection requests, 5 defines max quantity of connections waiting to be processed.
	 listen(sockfd,5);
	 clilen = sizeof(cli_addr);
	 //accept a new socket (accept is a blocking call, thus will wait for client connection to be established)
	 while(1){
		 newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr, &clilen);
		 //throw error if accept returns negative
		 if (newsockfd < 0) 
			  error("ERROR on accept");
		 //set the buffer to zero
		 bzero(buffer,256);
		 //read from the new socket fd into the buffer
		 n = read(newsockfd,buffer,255);
		 //if n is negative error is thrown
		 if (n < 0) error("ERROR reading from socket");
		 //display message
		 printf("Here is the message: %s
",buffer);
		 
		//write message to next host
		n = write(sendsockfd,buffer,strlen(buffer));
		if (n < 0) 
			error("ERROR writing to socket");
		bzero(buffer,256);
		 
		 //send ack of correct message delivery
		 n = write(newsockfd,"I got your message",18);
		 if (n < 0) error("ERROR writing to socket");
		 //close sockets to free up file descriptor table.
		 close(newsockfd);
	 }
     close(sockfd);
     return 0; 
}

##server2.c

/* A simple server in the internet domain using TCP
   The port number is passed as an argument */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>

void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
     int sockfd, newsockfd, portno;
     socklen_t clilen;
     char buffer[1600];
     struct sockaddr_in serv_addr, cli_addr;
     int n;
     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided
");
         exit(1);
     }

     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0) 
        error("ERROR opening socket");
     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);
     if (bind(sockfd, (struct sockaddr *) &serv_addr,
              sizeof(serv_addr)) < 0) 
              error("ERROR on binding");
while(argc = 0)
{
     listen(sockfd,5);
     clilen = sizeof(cli_addr);
     newsockfd = accept(sockfd, 
                 (struct sockaddr *) &cli_addr, 
                 &clilen);
     if (newsockfd < 0) 
          error("ERROR on accept");
     bzero(buffer,1600);
     n = read(newsockfd,buffer,1600);
     if (n < 0) error("ERROR reading from socket");
     printf("Here is the message: %s
",buffer);
     n = write(newsockfd,"I got your message",18);
     if (n < 0) error("ERROR writing to socket");
     }
     close(newsockfd);
     close(sockfd);

     return 0; 

}

Bài này có thể viết bằng java nhưng mình không biết về java bên computer networking

Mình xin càm ơn mọi người trong diễn đàn nhiều lắm

Mai Anh Dũng viết 20:18 ngày 30/09/2018

(chỉ có 1 file client.c)

Bạn viết như thế này có nghĩa là gì?

Dựa vào cái đề mình đã viết được file proxy nhưng mình cần file này vùa có chức năng server và proxy để tạo thành file server_relay

file proxy mục đích để làm gì?


Làm sao để test chương trình của bạn? Build như thế nào? chạy file nào trước file nào sau, step để test? Lỗi đang gặp phải là gì?

Khoa Le viết 20:26 ngày 30/09/2018

proxy.c chính là file file trung gian giữa client và server giống như giám sát viên vậy. ví dụ mình đăng comment lên vnexpress và nó hiện ra lời bình của bạn đang được xem xét nếu ok thì nó chuyển comment lên server (trường hơp này thì tự động)

trong bài mình cần kết hợp cả file server.c và proxy.c lại và tạo thành server_relay

khi chạy chương trình trên terminal của linux, mình cẩn 3 bảng terminal

  • 1 bảng cho server_relay và mã lênh là : ./server_relay port (ví dụ là ./server_relay 12345) là server chạy đầu tiên

  • 2 bảng cho file client.c cả hai bảng có mã lệnh là ./client hostname port (ví dụ là : ./client 192.168.1.17 12345) là file chạy thứ hai và ba để kết nói với server_relay

khi file client.c chay thì nó sẽ hiện lên:

Please enter the message:
: >hi

và cách thức hoạt động như trên đó bạn

chỉ có 1 file client.c có nghĩa là code 1 file clientc và nó đóng vai trò là client(1) và client(2) đang trỏ chuyện với nhau thông qua trung gian server_relay và tin nhắn của cà hai client hiện lên trên server_relay

mình mới học computer networking và C nên không hiểu nhiều nên mình không biết viết file server_relay.c

và lỗi hiện nay mình gặp là file client.c của mình chỉ có thể gửi 1 message và sau đó nó ngắt kết nối với file proxy.c

Mai Anh Dũng viết 20:18 ngày 30/09/2018

và lỗi hiện nay mình gặp là file client.c của mình chỉ có thể gửi 1 message và sau đó nó ngắt kết nối với file proxy.c

Ok, để mình compile chạy thử

khi chạy chương trình trên terminal của linux, mình cẩn 3 bảng terminal

Chạy trên cùng một máy là được?


Update:

Compile server nhưng chạy thì không hiển thị gì cả

gcc server2.c -o server2
./server2 12345

Kiểm tra process đang chạy cũng không thấy

datle c $ ps aux|grep server
datle     2195  0.0  0.0  12536   960 ?        Ss   10:20   0:00 /usr/lib/openssh/sftp-server
datle     2200  0.0  0.0  12536   952 ?        Ss   10:20   0:00 /usr/lib/openssh/sftp-server
datle     2548  0.0  0.0   7832   880 pts/8    S+   10:38   0:00 grep server
datle    25192  0.0  0.0  12536   912 ?        Ss   Sep28   0:00 /usr/lib/openssh/sftp-server
datle    30855  0.0  0.0  12536   908 ?        Ss   Sep28   0:00 /usr/lib/openssh/sftp-server

Chạy thử client thì bị lỗi

datle c $ ./client 192.168.88.11 12345
ERROR connecting: Connection refused

Mình làm sai bước nào?

Khoa Le viết 20:28 ngày 30/09/2018

command gcc sai rồi bạn

gcc -o server2 server2.c như vậy mới đúng

còn client.c không bị lỗi “ERROR connecting: Connection refused” câu này là do mình add vào

khi server.c không hoạt động thì đương nhiên khi đó client.c không thể kết nối thì mới hiện câu đó

Khoa Le viết 20:20 ngày 30/09/2018

./client 192.168.88.11 12345 câu này

192.168.88.11 cái này mình không biết

thường là ví dụ là vậy 192.168.1.11

bạn có thể kiểm tra hostname bằng command /sbin/ifconfig -a

Mai Anh Dũng viết 20:25 ngày 30/09/2018

gcc -o server2 server2.c hay gcc server2.c -o server2 đều là một, -o dùng để chỉ định tên file output. Đặt -o trước hay sau tên file .c không thành vấn đề.

còn client.c không bị lỗi “ERROR connecting: Connection refused” câu này là do mình add vào

khi server.c không hoạt động thì đương nhiên khi đó client.c không thể kết nối thì mới hiện câu đó

Dĩ nhiên mình biết điều đó, mình đang muốn nói là server không chạy

192.168.88.11 cái này mình không biết

thường là ví dụ là vậy 192.168.1.11

bạn có thể kiểm tra hostname bằng command /sbin/ifconfig -a

Máy mình ip 192.168.88.11

Khoa Le viết 20:25 ngày 30/09/2018

à mình tìm ra lỗi trong server2.c rồi đó là đoạn

while (argc = 0)

bạn sữa lại thành là argc > 0 là ok

Mai Anh Dũng viết 20:25 ngày 30/09/2018

while (argc = 0)

bạn sữa lại thành là argc > 0 là ok

OK, đã chạy được.

lỗi hiện nay mình gặp là file client.c của mình chỉ có thể gửi 1 message và sau đó nó ngắt kết nối với file proxy.c

Là bởi vì bạn gọi close(sockfd); ngay sau khi write(sockfd,buffer,strlen(buffer));. Gửi xong gói đầu thì tiên close và kết thúc chương trình rồi.

Nếu muốn giữ kết nối thì bạn phải dùng vòng lặp như while và không close cho tới khi xong việc(tức khi muốn thoát)

Trong code của server có đoạn này để lặp chờ tin từ client

while(argc > 0)
{
        listen(sockfd,5);
        clilen = sizeof(cli_addr);
        newsockfd = accept(sockfd, 
                (struct sockaddr *) &cli_addr, 
                &clilen);
        if (newsockfd < 0) 
                error("error on accept");
        bzero(buffer,1600);
        n = read(newsockfd,buffer,1600);
        if (n < 0) error("error reading from socket");
        printf("here is the message: %s\n",buffer);
        n = write(newsockfd,"i got your message",18);
        if (n < 0) error("error writing to socket");
}
Khoa Le viết 20:28 ngày 30/09/2018

mình thữ rồi bạn và nó hiện kết qua như thế này

enter message : hi
i got message
enter message : hello
(mất câu i got measage và không hiển thị tiếp theo trên server2.c message)

Mai Anh Dũng viết 20:20 ngày 30/09/2018

Bạn phải đưa sự thay đổi của bạn lên chứ?

Khoa Le viết 20:21 ngày 30/09/2018

Đây nè bạn

if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
{ 
        error("ERROR connecting"); 
    return 1;
} 
while(argc > 0){ 
    printf("Please enter the message: "); 
    bzero(buffer,256); 
    fgets(buffer,255,stdin); 
    n = write(sockfd,buffer,strlen(buffer)); 
    if (n < 0)  
         error("ERROR writing to socket"); 
    bzero(buffer,256); 
    n = read(sockfd,buffer,255); 
    if (n < 0)  
         error("ERROR reading from socket"); 
    printf("%s\n",buffer); 
} 
Mai Anh Dũng viết 20:24 ngày 30/09/2018

Sửa lại thế này xem

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 

void error(const char *msg)
{
        perror(msg);
        exit(1);
}

int main(int argc, char *argv[])
{
        int sockfd, portno, n;
        struct sockaddr_in serv_addr;
        struct hostent *server;

        char buffer[256];
        if (argc < 3) {
                fprintf(stderr,"usage %s hostname port\n", argv[0]);
                exit(0);
        }
        portno = atoi(argv[2]);
        server = gethostbyname(argv[1]);
        if (server == NULL) {
                fprintf(stderr,"ERROR, no such host\n");
                exit(0);
        }
        bzero((char *) &serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        bcopy((char *)server->h_addr, 
                        (char *)&serv_addr.sin_addr.s_addr,
                        server->h_length);
        serv_addr.sin_port = htons(portno);

        while (1) {
                printf("Please enter the message: ");
                bzero(buffer,256);
                fgets(buffer,255,stdin);
                sockfd = socket(AF_INET, SOCK_STREAM, 0);
                if (sockfd < 0) 
                        error("ERROR opening socket");
                if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
                        error("ERROR connecting");
                n = write(sockfd,buffer,strlen(buffer));
                if (n < 0) 
                        error("ERROR writing to socket");
                bzero(buffer,256);
                n = read(sockfd,buffer,255);
                if (n < 0) 
                        error("ERROR reading from socket");
                printf("%s\n",buffer);
                close(sockfd);
        }
        
        return 0;
}
Khoa Le viết 20:17 ngày 30/09/2018

wow hot hoat động rồi vậy lỗi nằm ở chỗ nào vậy bạn

mình đã kiểm tra và chỉ cần 1 file clien.c thì sẽ chay được nhiều client trên terminal và tất cả đều truyền đến server.c với cùng port, bạn bạn cho mình hỏi là làm thế nào để client(1) nhận được
tin nhắn từ những client khác và ngược lại một cách chính xác,

ý tưởng của mình là tạo ra một function server ngay trên chính client và server.c sẽ giữ thêm chức năng chuyển tin nhắn của proxy.c nhưng làm thế nào vậy bạn

Mai Anh Dũng viết 20:27 ngày 30/09/2018

Mình không giữ được connection nên cho tạo lại connection cho mỗi message, bạn đọc code sẽ thấy.

Khoa Le viết 20:29 ngày 30/09/2018

vậy lỗi là ở chổ do connect nằm ngoài loop nên nó không lặp lại kết nối hả bạn

vũ xuân quân viết 20:24 ngày 30/09/2018

Mình không giữ được connection nên cho tạo lại connection cho mỗi message

Cho mình hỏi, mỗi lần gửi message mà tạo connection hoài thì có bị tràn bộ nhớ hay không.
Có cần phải close connection lại không ? hay hệ thống tự làm.

Mai Anh Dũng viết 20:26 ngày 30/09/2018

vậy lỗi là ở chổ do connect nằm ngoài loop nên nó không lặp lại kết nối hả bạn

Đúng rồi.

Cho mình hỏi, mỗi lần gửi message mà tạo connection hoài thì có bị tràn bộ nhớ hay không.

Tràn bộ nhớ tùy thuộc vào mỗi connection mình có cấp vùng nhớ hay không. Nhưng riêng mỗi connection được tạo ra mà không close thì để lâu sẽ dẫn đến hết file descriptor. Số lượng file descriptor tuy rất lớn nhưng có giới hạn. Mình có thể xem số lượng ở đây

cat /proc/sys/fs/file-max

Máy debian mình đang dùng là 813549, còn trên embedded board mình đang dùng là 12601. Ngoài ứng dụng của mình ra thì còn có nhiều ứng dụng khác cũng sử dụng file descriptor nữa. Đóng mở file là sử dụng thôi.

Có cần phải close connection lại không ? hay hệ thống tự làm.

Có, mình phải close.

mình đã kiểm tra và chỉ cần 1 file clien.c thì sẽ chay được nhiều client trên terminal và tất cả đều truyền đến server.c với cùng port, bạn bạn cho mình hỏi là làm thế nào để client(1) nhận đượctin nhắn từ những client khác và ngược lại một cách chính xác,

Bởi vì mỗi khi có một connect từ client, hàm accept sẽ tạo ra một file descriptor nữa. Trong trường hợp này nó là newsockfd , có lẽ là viết tắt của new socket file descriptor. Trên server sẽ trả lời thông qua socket này.

ý tưởng của mình là tạo ra một function server ngay trên chính client và server.c sẽ giữ thêm chức năng chuyển tin nhắn của proxy.c nhưng làm thế nào vậy bạn

Code hiện tại chỉ cho phép client tạo socket và write lên đó, sau đó đọc trả lời từ server rồi hủy, không giữ connection.

Để forward được message thì có 3 cách

Cách 1: cho client listen giống như server – phiền, không cần thiết
Cách 2: giữ connection với server để server có thể gửi tin ngược trở lại bất cứ lúc nào? – khá ổn, chỉ có điều là phải code lại để giữ connection
Cách 3: lâu lâu gửi lại lên server hỏi xem tao có nhận được tin nhắn gì không? – dễ nhất, sửa lại implementation hiện tại, thêm cái nick_name nữa là được.

Khoa Le viết 20:17 ngày 30/09/2018

cách 3 thì nó không với yêu cầu đề bài lắm, còn cách 1 như bạn nói

vậy nên chỉ còn cách hai là hợp lý, nhưng có điều là không phải gửi tin ngược trở lại mà là gửi tới một client đang chạy trên một termial khác, ngoài ra mình nghĩ không nhất thiết phải giữ connect với server vì hai file hoat động như thế này

client (1) <==> server_relay <==> client (2)

nên mình nghĩ là chỉ cần khi nhập tin nhắn từ 1 client thì nó sẽ truyền qua server và đi đến một clien khác và kết thúc 1 vòng loop vì 1 file clien.c thì chay được nhiều client trên terminal và tất cả đều truyền đến server.c với cùng port

bạn giúp mình code đoạn này được không

Mai Anh Dũng viết 20:24 ngày 30/09/2018

Nếu mình rảnh thì mình có thể code toàn bộ bài này, nhưng mình khá bận mình chỉ có thể xem code của bạn rồi hướng dẫn sửa lại cho phù hợp thôi. Chứ còn code hết cả bài thì vừa mất thời gian mà bạn cũng không học được, bạn đang học đúng không?

À tiện đây cũng nhắc bạn luôn là bạn đừng tạo nhiều topic với cùng nội dung, hệ thống sẽ đánh giá bạn là spam và ẩn bài của bạn đấy. Nếu mình rảnh thì mình sẽ xem, còn nếu mình bận thì chắc nhờ các bạn khác vậy.

Khoa Le viết 20:24 ngày 30/09/2018

không, mình hoc chuyên về ứng dụng java và lập trinh web, tự nhiên đâu ra môn này là môn bắt buộc hoc, trong khi mình chả có tí kiến thức gì về C cả, đống kia là mình mò 24/7 tuần trước mới viết được, toàn tiếng anh không :’(

mình chỉ là không biết code phần gửi tin nhăn từ server đến client(2) thui sao cho chính xác nò toan gửi ngược lai client(1)

:’(

Bài liên quan
0