Hi to everyone!
I want to create a simple chat application using udp sockets. I want to have 2 applications that will be able to talk to each other.In particular app1 will send msgs to p2 and p2 will display them in stdout. Then maybe p2 sends a msg to p1... etc..

for the initialization part i have the following:

-->> server part initialization.

if (argc != 4)
err_quit("usage: udpcli <IPaddress> <his-Port> <mine-Port>\n");
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);

memset( &servaddr, 0, sizeof(servaddr) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(atoi( argv[3]) );
//servaddr.sin_port = htons( SERVER_PORT );
Bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr) );

printf("Peer-Server initiated...listening in %s  and port %d\n\t waiting for clients\n",
		argv[1], ntohs(servaddr.sin_port));

-->> client part initialization

memset( &hisaddr, 0, sizeof(hisaddr) );
	hisaddr.sin_family = AF_INET;
	hisaddr.sin_port = htons( atoi(argv[3]) );
	Inet_pton( AF_INET, argv[1], &hisaddr.sin_addr);

-->> fork so that the application will be able to send and receive messages.

if((childpid = fork()) == 0)
	{
		//send message to peer
		peer_dg_send(stdin, sockfd, (struct sockaddr *) &hisaddr, sizeof(hisaddr));
	}
	else
	{
		//receive message from peer
		peer_dg_receive(sockfd, (struct sockaddr *)  &hisaddr, sizeof(hisaddr) ); 

	}

here are some extra functions:

void peer_dg_receive(int sockfd, struct sockaddr * pcliaddr, socklen_t clilen)
{
	int n;
	socklen_t len;
	char mesg[MAXLINE];

	for(;;)
	{
		len = clilen;
		n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);

		mesg[n] = 0;	/* null terminate */
		//Fputs(mesg, stdout);
		printf("Received: %s", mesg);
	}
}

void peer_dg_send(FILE *fp, int sockfd, struct sockaddr *pservaddr, socklen_t servlen)
{
	int	n;
	char	sendline[MAXLINE];

	while (Fgets(sendline, MAXLINE, fp) != NULL) 
	{
		Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
	}

}

also the functions with the first letter capitalized are from stevens's "unix network programming".


I have to questions in the above scheme:
First, is this the "best" way to tackle the chat problem? or am i missing something in the big picture

Second, it fails to work in the following sense:
each time i try to send something from app1 to app2.
app2 never receives the msg... but instead app1 does! why? and how can i fix it...

any ideas {even if not complete} are welcome!

thanks for your help,
nicolas

Recommended Answers

All 6 Replies

First, is this the "best" way to tackle the chat problem?

no. it is unlikely that someone would want to set up a chat between two processes on the same machine, sharing stdin and stdout between them. write two separate programs; the server and the client. which can be run on different machines.


> each time i try to send something from app1 to app2.
> app2 never receives the msg... but instead app1 does! why?

i've only had a cursory look, but:
the forked child process has its own copy of the parent's descriptors.
but these descriptors reference the same underlying objects as the parent.
both the parent and child processes are using the same socket, same stdin etc.

First, is this the "best" way to tackle the chat problem?

no. it is unlikely that someone would want to set up a chat between two processes on the same machine, sharing stdin and stdout between them.

sorry i didn't express myself correctly.

University assignment dictates that we do a chat "system" with 2 udp connected peers. In this sense we can't have
a single server, but a lot of peers. So i figured that each peer will be both a server for the other peer,
and a client inorder to talk to some other peer.

i read somewhere that sockets are full duplex, so initially i tried to build an architecture like this:

+---------------+		+---------------+
|		|		|		|
|  peer1	|		|	peer2	|
|		|port1	   port2|		|
|	sock----|---------------|----sock	|
|		|		|		|
|		|		|		|
+---------------+		+---------------+

Where each peer will fork and run like this

if((childpid = fork()) == 0)
{
	//sending messages to other peer
}
else
{
	//receiving message from other peer
}

Unfortunatelly this didn't work. The reason was that no message ever reached the other peer. i.e. if i tried to send
from peer1 to peer2, then peer1 sent the message and peer1-child received it.

So one question is: is it possible to have a full duplex communication using only one process?
If yes, how?
If not, why?

So i decided to change the architecture and create 2 sockets. The scheme i created resembled this:

+-------------------------------+		+-----------------------+
|				|		|			|
|  peer1			|		|		peer2	|
|				|port1	   port2|			|
|	listening socket -------|---------------|----sending socket	|
|				|		|			|
|				|		|			|
|				|port2	   port1|			|
|	sending socket----------|---------------|----listening socket	|
|				|		|			|
|				|		|			|
+-------------------------------+		+-----------------------+

this way when the process forks.The child is responsible for sending and the parent for listening.
With this scheme everything works great! There is only one small problem, concerning the peer as whole.
PeerX process runs on a terminal, so the terminal is used for stdout {receiving messages}
and stdin {writing messages}. So there are problems while you are writing a message and at the same time
you receive one.

In this point i have to more questions:

1) Is there anyway to divert stdin {or stdout} to another terminal. I know that i can divert it to a file
but because i want live communication, it isn't convienient...

2) Could this diversion be "easily" done, by using ncurses and creating something like this:

+-------------------------------+
|				|
|				|
|	stdout			|
|	{messages received}	|
|				|
+-------------------------------+
|				|
|				|
|	stdin			|
|	{messages i type}	|
|				|
+-------------------------------+

any ideas are welcome, even if not complete....

-nicolas

> i read somewhere that sockets are full duplex, so initially i tried to build an architecture like this:
a socket is *one* end of a two-way communications link.
a BSD socket maintains a separate send buffer and a receive buffer, and is full-duplex (you can both send and receive data on it).
to send or receive data, you need two sockets (one at either end).
in your first example, you are trying to use the *same* socket at both ends.

create *two* sockets, one for the client and another for the server.
(eg. socketpair does this for UNIX sockets. http://www.freebsd.org/cgi/man.cgi?query=socketpair)
after forking, close the client socket in the server, and the server socket in the client.
and things should be ok.

+---------------+		+---------------+
|		|		|		|
|  peer1	|		|	peer2	|
|		|port1	   port2|		|
|      sock1----|---------------|----sock2	|
|		|		|		|
|		|		|		|
+---------------+		+---------------+

> Could this diversion be "easily" done, by using ncurses
yes.

Thanks for your answers vijayan,

a socket is *one* end of a two-way communications link.
a BSD socket maintains a separate send buffer and a receive buffer, and is full-duplex (you can both send and receive data on it). to send or receive data, you need two sockets (one at either end).

nice explanation!

but:
>http://en.wikipedia.org/wiki/Duplex_(telecommunications)

in your first example, you are trying to use the *same* socket at both ends.

As far as i can understand full-duplex is called when you can send and receive at the same time. So if a socket is a full duplex communications endpoint then you can send and receive data at the same time {from the same endpoint}. In the first example i wanted to do exactly this thing that is send and listen at the same time. Because these procedures are blocking, i thought that a way to circumvent this behaviour was to create a fork...

create *two* sockets, one for the client and another for the server.

but i don't have a client-server scheme but a peer-to-peer scheme. The way i see it each peer must be a client and a server at the same time. Is there any other way to make peer-to-peer conversation? I am asking because it is the first i get exposed to the concept of p2p communication and not client-server

(eg. socketpair does this for UNIX sockets. http://www.freebsd.org/cgi/man.cgi?query=socketpair)
after forking, close the client socket in the server, and the server socket in the client.
and things should be ok.

+---------------+		+---------------+
|		|		|		|
|  peer1	|		|	peer2	|
|		|port1	   port2|		|
|      sock1----|---------------|----sock2	|
|		|		|		|
|		|		|		|
+---------------+		+---------------+

I can't use socketpair because according to the man page

This call is currently implemented only for the UNIX domain.

and i want internet domain....

> Could this diversion be "easily" done, by using ncurses
yes.

I will try to do it this weekend...

Thanks again for your effort and your time,
Although i have a found a kind of solution to this problem {the one i posted in my second post with the use of 2 sockets per peer}, i keep asking because either i don't understand the concept of a socket, port,etc,... or i am doing something terribly wrong...

with regards,
nicolas

PS: as a side effect question what is the relation between a socket and a port. I mean a socket is a special kind of file descriptor, since when i use file descriptors i don't use ports, why we have to use ports when we use a socket?

> what is the relation between a socket and a port.
> I mean a socket is a special kind of file descriptor
> when i use file descriptors i don't use ports
> why we have to use ports when we use a socket?

a socket has no relation to a port.

a socket is created with the socket function has no name. this is no different from other file descriptors. a file descriptor also has no name. for example, stdin, stdout, stderr are file descriptors.
you use the open function to create a descriptor, the name that you give is the name of a file in the filesystem (which has an existence independant of file descriptors).

a remote process has no way to refer to a socket until a name (an address) is bound to it. an address is given to a socket via the bind function call. the address serves as a unique name using which a socket can be referred to.

In the UNIX domain, an address is simply a filesystem path. eg.

sockaddr_un addr;
 ...
std::strcpy( addr.sun_path, "/tmp/my_socket" ) ;
addr.sun_family = AF_UNIX ;
bind ( ... (sockaddr*) &addr, .... ) ;

The file name referred to is created as a socket in the system file name space. (write permission in the directory is required). these can be deleted with unlink().

in the Internet domain, the name is a tuple consisting of the IP address and a port number. a connection is a *unique* ordered tuple ( protocol, local ip address, local port, remote ip address, remote port). ports are an artifact of the addressing scheme to facilitate distinguishing between different connections on the same host (by composing unique tuples - at least the port is different - for each connection).

the same socket can be used to send and receive data at the same time. the following example would work, though we use the same socket:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <iostream>
#include <cstring>
#include <string>

enum { RECV_PORT = 5000, MSGSIZE = 1024 };

socklen_t addr_len = sizeof(sockaddr) ;

int listener( int socket_fd )
{
    sockaddr_in my_addr ;
    std::memset( &my_addr, 0, sizeof(my_addr) ) ;
    my_addr.sin_family = AF_INET ;
    my_addr.sin_port = htons( RECV_PORT ) ;
    my_addr.sin_addr.s_addr = INADDR_ANY ;

    if ( bind( socket_fd, (sockaddr*)&my_addr, addr_len ) != 0 )
       return 2 ;


    while( true )
    {
        char recv_data[MSGSIZE+1] ;
        sockaddr_in client_addr ;

        int bytes_recd = recvfrom( socket_fd, recv_data, MSGSIZE, 0,
                                (sockaddr*)&client_addr, &addr_len ) ;
        if( bytes_recd == -1 ) break ;
        recv_data[bytes_recd] = '\0' ;

        std::cout << "from " << inet_ntoa(client_addr.sin_addr)
                  << ':' << ntohs(client_addr.sin_port) << " - "
                  << recv_data << std::endl ;
    }
    return 0 ;
}

int sender( int socket_fd )
{
    std::cout << "address of peer? " ;
    std::string address ; std::cin >> address >> std::ws ;

    sockaddr_in peer_addr ;
    std::memset( &peer_addr, 0, sizeof(peer_addr) ) ;
    peer_addr.sin_family = AF_INET ;
    peer_addr.sin_port = htons( RECV_PORT ) ;
    peer_addr.sin_addr.s_addr =
             *(in_addr_t*)(gethostbyname( address.c_str() )->h_addr) ;

    std::string send_str ;
    while( std::getline( std::cin, send_str ) )
    {
      send_str.resize(MSGSIZE) ;
      sendto( socket_fd, send_str.c_str(), MSGSIZE, 0,
            (sockaddr*)&peer_addr, addr_len ) ;
    }
    return 0 ;
}

int main()
{
  int socket_fd = socket( AF_INET, SOCK_DGRAM, 0 ) ;
  if( socket_fd == -1 ) return 1 ;
  return fork() == 0 ? listener( socket_fd ) : sender( socket_fd ) ;
}

ps1: run with peer address "127.0.0.1" (localhost) for testing.
ps2: if you have follow-up questions that you want me to answer, do not post them in daniweb. i keep away from web sites that behave as if Internet Explorer is the only browser that i should use. (this post is from IE/Wine). you could use http://forums.devx.com/forumdisplay.php?s=&forumid=110
instead, which is the (only) other C++ student's forum that i look at on a somewhat regular basis.

econd, it fails to work in the following sense:
each time i try to send something from app1 to app2.
app2 never receives the msg... but instead app1 does! why? and how can i fix it...

I Just tried the code given above by that I can only send and receive text messeges between two machine.
But I want same applcation to send and receive voice rather than text messege (just like voice chat).
please tell the apropriate modification to the code so i can do voice chat between two machines.

Thanks,

Ambresh Biradar

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.