A few illustrated reminds about UDP TCP sockets.
For complete explanations, please have a look at : https://web.enib.fr/~harrouet
Every following examples can be downloaded here :
just write ‘make’ in a terminal to compile all of them.
|
|
Considering we have a Linux PC + another Linux PC or Rasperry PI :
If you want to test with one PC ( with local adress 127.0.0.1) , consider this drawing :
|
|
|
|
Datas are not necessarly characters ; sending binary datas supposes knowing byte order.
That’s the purpose of htonl and ntohs functions.
|
|
|
|
Since the receive function is blocking, the SELECT function permits switching between several incoming sources.
|
|
#include "main.h"
//======================================================================
// MAIN
//======================================================================
int main(int argc, char **argv)
{
int txtPortNumber,binPortNumber;
struct sockaddr_in myAddr;
fd_set rdSet;
char msg[0x100];
struct sockaddr_in fromAddr;
int32_t value;
//---- check command line arguments ----
if(argc!=3)
{ fprintf(stderr,"usage: %s txt_port bin_port\n",argv[0]); exit(1); }
//---- extract local port numbers ----
if(sscanf(argv[1],"%d",&txtPortNumber)!=1)
{ fprintf(stderr,"invalid port %s\n",argv[1]); exit(1); }
if(sscanf(argv[2],"%d",&binPortNumber)!=1)
{ fprintf(stderr,"invalid port %s\n",argv[1]); exit(1); }
//---- create UDP socket (text) ----
int txtUdpSocket=socket(PF_INET,SOCK_DGRAM,0);
if(txtUdpSocket==-1)
{ perror("socket"); exit(1); }
// ... bound to any local address on the specified port
myAddr.sin_family=AF_INET;
myAddr.sin_port=htons(txtPortNumber);
myAddr.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(txtUdpSocket,(struct sockaddr *)&myAddr,sizeof(myAddr))==-1)
{ perror("bind"); exit(1); }
// ... allowing broadcast (optional)
int on=1;
if(setsockopt(txtUdpSocket,SOL_SOCKET,SO_BROADCAST,&on,sizeof(int))==-1)
{ perror("setsockopt"); exit(1); }
//---- create UDP socket (binary) ----
int binUdpSocket=socket(PF_INET,SOCK_DGRAM,0);
if(binUdpSocket==-1)
{ perror("socket"); exit(1); }
// ... bound to any local address on the specified port
myAddr.sin_family=AF_INET;
myAddr.sin_port=htons(binPortNumber);
myAddr.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(binUdpSocket,(struct sockaddr *)&myAddr,sizeof(myAddr))==-1)
{ perror("bind"); exit(1); }
// ... allowing broadcast (optional)
on=1;
if(setsockopt(binUdpSocket,SOL_SOCKET,SO_BROADCAST,&on,sizeof(int))==-1)
{ perror("setsockopt"); exit(1); }
for(;;)
{
//---- wait for incoming informations ----
FD_ZERO(&rdSet);
FD_SET(0,&rdSet); // standard input
FD_SET(txtUdpSocket,&rdSet);
FD_SET(binUdpSocket,&rdSet);
int maxFd=txtUdpSocket>binUdpSocket ? txtUdpSocket : binUdpSocket;
// SELECT
if(select(maxFd+1,&rdSet,(fd_set *)0,(fd_set *)0,(struct timeval *)0)==-1)
{ perror("select"); exit(1); }
//---- display standard input (if available) ----
if(FD_ISSET(0,&rdSet))
{
if(fgets(msg,0x100,stdin))
{
printf("standard input: %s\n",msg);
}
}
//---- receive text message (if available) ----
if(FD_ISSET(txtUdpSocket,&rdSet))
{
socklen_t len=sizeof(fromAddr);
int r=recvfrom(txtUdpSocket,msg,0x100,0,(struct sockaddr *)&fromAddr,&len);
if(r==-1)
{ perror("recvfrom"); exit(1); }
//---- display message and source address/port ----
msg[r]='\0';
printf("text message from %s:%d : %s\n", inet_ntoa(fromAddr.sin_addr),ntohs(fromAddr.sin_port),msg);
}
//---- receive 32-bit value (if available) ----
if(FD_ISSET(binUdpSocket,&rdSet))
{
socklen_t len=sizeof(fromAddr);
int r=recvfrom(binUdpSocket,&value,4,0,(struct sockaddr *)&fromAddr,&len);
if(r==-1)
{ perror("recvfrom"); exit(1); }
//---- convert to host format 32-bit integer ----
if(r<4)
{ fprintf(stderr,"4 bytes expected\n"); exit(1); }
value=ntohl(value);
//---- display value and source address/port ----
printf("binary value from %s:%d : %d\n", inet_ntoa(fromAddr.sin_addr),ntohs(fromAddr.sin_port),value);
}
}
close(txtUdpSocket);
close(binUdpSocket);
return 0;
}
//----------------------------------------------------------------------------
TCP is a CONNECTED protocole, so before sending and receiving datas, the server must accept a client connection request.
For the moment the actual client must end the connection before the server accepts a new client.
{: .image_center height=“auto” width=“1000px” }
|
|
|
|
The Select Function permits switching between several sockets and listening for new TCP connections.
|
|
#include "main.h"
//======================================================================
// MAIN
//======================================================================
int main(int argc, char **argv)
{
int portNumber;
struct sockaddr_in myAddr;
int ok;
int clients[0x100];
int nbClients=0;
int i;
struct sockaddr_in fromAddr;
char buffer[0x100];
//---- check command line arguments ----
if(argc!=2)
{ fprintf(stderr,"usage: %s port\n",argv[0]); exit(1); }
//---- extract local port number ----
if(sscanf(argv[1],"%d",&portNumber)!=1)
{ fprintf(stderr,"invalid port %s\n",argv[1]); exit(1); }
//---- create listen socket ----
int listenSocket=socket(PF_INET,SOCK_STREAM,0);
if(listenSocket==-1)
{ perror("socket"); exit(1); }
// ... avoiding timewait problems (optional)
int on=1;
ok = setsockopt(listenSocket,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(int));
if( ok == -1 ){ perror("setsockopt"); exit(1); }
// ... bound to any local address on the specified port
myAddr.sin_family=AF_INET;
myAddr.sin_port=htons(portNumber);
myAddr.sin_addr.s_addr=htonl(INADDR_ANY);
ok = bind(listenSocket,(struct sockaddr *)&myAddr,sizeof(myAddr));
if( ok == -1 ){ perror("bind"); exit(1); }
// ... listening connections
ok = listen(listenSocket,10);
if( ok == -1 ){ perror("listen"); exit(1); }
for(;;)
{
//---- wait for new connections or incoming messages ----
fd_set rdSet;
FD_ZERO(&rdSet);
FD_SET(listenSocket,&rdSet);
int maxFd=listenSocket;
for(i=0;i<nbClients;++i)
{
FD_SET(clients[i],&rdSet);
if(clients[i]>maxFd) maxFd=clients[i];
}
ok = select(maxFd+1,&rdSet,(fd_set *)0,(fd_set *)0,(struct timeval *)0);
if( ok == -1 ){ perror("select"); exit(1); }
//---- accept and store new connection (if available) ----
if(FD_ISSET(listenSocket,&rdSet))
{
socklen_t len=sizeof(fromAddr);
int dialogSocket=accept(listenSocket,(struct sockaddr *)&fromAddr,&len);
if(dialogSocket==-1)
{ perror("accept"); exit(1); }
printf("new connection from %s:%d\n",inet_ntoa(fromAddr.sin_addr),ntohs(fromAddr.sin_port));
clients[nbClients++]=dialogSocket;
}
//---- dialog with each client providing a message ----
for(i=nbClients;i--;)
{
int dialogSocket=clients[i];
if(FD_ISSET(dialogSocket,&rdSet))
{
//---- receive and display message from client (if available) ----
int nb=recv(dialogSocket,buffer,0x100,0);
if(nb<=0)
{
printf("client disconnected\n");
close(dialogSocket);
clients[i]=clients[--nbClients];
}
else
{
buffer[nb]='\0';
printf("%s\n",buffer);
//---- send reply to client ----
nb=sprintf(buffer,"%d bytes received\n",nb);
ok = send(dialogSocket,buffer,nb,0);
if( ok == -1 ){ perror("send"); exit(1); }
}
}
}
}
close(listenSocket);
return 0;
}
//----------------------------------------------------------------------------
A more flexible server than the ‘select’ one : allowing a thread for each client.
The FOR loop in the main() wait on new connection requests.
|
|
#include "main.h"
//======================================================================
void * dialogThread(void *arg)
{
char buffer[0x100];
int ok;
pthread_detach(pthread_self());
//---- obtain dialog socket from arg ----
int dialogSocket=*(int*)arg;
free(arg);
for(;;)
{
//---- receive and display message from client ----
int nb=recv(dialogSocket,buffer,0x100,0);
if(nb<=0) { break; }
buffer[nb]='\0';
printf("%s\n",buffer);
//---- send reply to client ----
nb=sprintf(buffer,"%d bytes received\n",nb);
ok = send(dialogSocket,buffer,nb,0);
if( ok == -1 ){ perror("send"); exit(1); }
}
//---- close dialog socket ----
printf("client disconnected\n");
close(dialogSocket);
return (void *)0;
}
//======================================================================
// MAIN
//======================================================================
int main(int argc, char **argv)
{
int ok;
int portNumber;
struct sockaddr_in myAddr;
struct sockaddr_in fromAddr;
//---- check command line arguments ----
if(argc!=2)
{ fprintf(stderr,"usage: %s port\n",argv[0]); exit(1); }
//---- extract local port number ----
ok = sscanf(argv[1],"%d",&portNumber);
if( ok == -1 ){ fprintf(stderr,"invalid port %s\n",argv[1]); exit(1); }
//---- create listen socket ----
int listenSocket=socket(PF_INET,SOCK_STREAM,0);
if(listenSocket==-1)
{ perror("socket"); exit(1); }
// ... avoiding timewait problems (optional)
int on=1;
ok = setsockopt(listenSocket,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(int));
if( ok == -1 ){ perror("setsockopt"); exit(1); }
// ... bound to any local address on the specified port
myAddr.sin_family=AF_INET;
myAddr.sin_port=htons(portNumber);
myAddr.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(listenSocket,(struct sockaddr *)&myAddr,sizeof(myAddr))==-1)
{ perror("bind"); exit(1); }
// ... listening connections
if(listen(listenSocket,10)==-1)
{ perror("listen"); exit(1); }
for(;;)
{
//---- accept new connection ----
socklen_t len=sizeof(fromAddr);
int dialogSocket=accept(listenSocket,(struct sockaddr *)&fromAddr,&len);
if(dialogSocket==-1)
{ perror("accept"); exit(1); }
printf("new connection from %s:%d\n",
inet_ntoa(fromAddr.sin_addr),ntohs(fromAddr.sin_port));
//---- start a new dialog thread ----
pthread_t th;
int *arg=(int *)malloc(sizeof(int));
*arg=dialogSocket;
if(pthread_create(&th,(pthread_attr_t *)0,dialogThread,arg))
{ fprintf(stderr,"cannot create thread\n"); exit(1); }
}
close(listenSocket);
return 0;
}
//----------------------------------------------------------------------------