Basic flow of C/S program based on TCP protocol

Posted by mdemetri2 on Wed, 23 Feb 2022 02:59:37 +0100

Server communication process

Server:
	1,adopt socket()Function to create a to receive a connection request socket
	2,To construct the host connection address sockaddr_in structural morphology,include sin_family,sin_port,sin_addr Three members
	3,binding sockaddr_in Structure and socket
	4,adopt listen()Function will stocket Set to listening mode
	5,call accept()The function waits to receive the connection request from the client and returns the information used to transfer information stocket
	6,Communicate with clients
	7,call close()close socket

Basic communication example of TCP server

// Simple TCP loopback service server example
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
 
int main(int argc,char *argv[])
{
  if (argc!=2)
  {
    printf("Using:./server port\nExample:./server 5005\n\n"); return -1;
  }
 
  // Step 1: create a socket for receiving connection requests through the socket() function
  int listenfd;
  if ( (listenfd = socket(AF_INET,SOCK_STREAM,0))==-1) { perror("socket"); return -1; }
 
  // Step 2: construct the SOCKADDR of the host connection address_ In structure, including sin_family,sin_port,sin_addr three members
  struct sockaddr_in servaddr;    // Data structure of server address information.
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family = AF_INET;  // The protocol family can only be AF in socket programming_ INET. 
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);          // Any ip address.
  //servaddr.sin_addr.s_addr = inet_addr("111.111.111.111"); //  Specify the ip address.
  servaddr.sin_port = htons(atoi(argv[1]));  // Specify the communication port.
	
	// Step 3: bind sockaddr_in structure and socket
  if (bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 )
  { perror("bind"); close(listenfd); return -1; }
 
  // Step 4: set the bucket to the listening mode through the listen() function
  if (listen(listenfd,5) != 0 ) { perror("listen"); close(listenfd); return -1; }
 
  // Step 5: call the accept () function to wait for receiving the connection request from the client and return the bucket used to transfer information
  // Here, you can also call advanced IO functions or use multi process / thread mode to communicate with multiple clients at the same time
  int  clientfd;                  // The socket of the client.
  int  socklen=sizeof(struct sockaddr_in); // struct sockaddr_ Size of in
  struct sockaddr_in clientaddr;  // Address information of the client.
  clientfd=accept(listenfd,(struct sockaddr *)&clientaddr,(socklen_t*)&socklen);
  printf("Client(%s)Connected.\n",inet_ntoa(clientaddr.sin_addr));
 
  // Step 6: communicate with the client and reply ok after receiving the message sent by the client.
  char buffer[1024];//Create buffer
  while (1)
  {
    int iret;
    memset(buffer,0,sizeof(buffer));
    if ( (iret=recv(clientfd,buffer,sizeof(buffer),0))<=0) // Receive the request message from the client.
    {
       printf("iret=%d\n",iret); break;  
    }
    printf("receive:%s\n",buffer);
 
    strcpy(buffer,"ok");
    if ( (iret=send(clientfd,buffer,strlen(buffer),0))<=0) // Send response results to the client.
    { perror("send"); break; }
    printf("send out:%s\n",buffer);
  }
 
  // Step 7: call close() to close the socket
  close(listenfd); 
  close(clientfd);
}

It should be noted here that if multiple sockets for communicating with clients are created at the same time in step 5, pay attention to releasing the sockets at the end of communication with each client.

Client communication flow

client:
	1,adopt socket()Function to create a for requests and communications socket
	2,To construct the host link address sockaddr_in Structure, including sin_family,sin_port,sin_addr Three members
	3,call connect()Function sends a connection request to the server
	4,Communicate with the server
	5,call close()close socket

TCP client basic communication example

// Simple loopback service client example
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
 
int main(int argc,char *argv[])
{
  if (argc!=3)
  {
    printf("Using:./client ip port\nExample:./client 127.0.0.1 5005\n\n"); return -1;
  }
 
  // Step 1: create a socket for request and communication through the socket () function
  int sockfd;
  if ( (sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) { perror("socket"); return -1; }
 
  // Step 2: construct the SOCKADDR of the host link address_ In structure, including sin_family,sin_port,sin_addr three members
  struct hostent* h;
  if ( (h = gethostbyname(argv[1])) == 0 )   // Specify the ip address of the server.
  { printf("gethostbyname failed.\n"); close(sockfd); return -1; }
  struct sockaddr_in servaddr;
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons(atoi(argv[2])); // Specify the communication port of the server.
  memcpy(&servaddr.sin_addr,h->h_addr,h->h_length);
	
	// Step 3: call the connect() function to send a connection request to the server
  if (connect(sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr)) != 0)  // Initiate a connection request to the server.
  { perror("connect"); close(sockfd); return -1; }
 
  char buffer[1024];
 
  // Step 4: communicate with the server, send a message, wait for a reply, and then send the next message.
  for (int ii=0;ii<3;ii++)
  {
    int iret;
    memset(buffer,0,sizeof(buffer));
    sprintf(buffer,"This is the second%d Messages, number%03d. ",ii+1,ii+1);
    if ( (iret=send(sockfd,buffer,strlen(buffer),0))<=0) // Send request message to the server.
    { perror("send"); break; }
    printf("send out:%s\n",buffer);
 
    memset(buffer,0,sizeof(buffer));
    if ( (iret=recv(sockfd,buffer,sizeof(buffer),0))<=0) // Receive the response message from the server.
    {
       printf("iret=%d\n",iret); break;
    }
    printf("receive:%s\n",buffer);
  }
 
  // Step 5: call close() to close the socket
  close(sockfd);
}

Summary: illustrate the simple TCP communication process

Summary: function parameters and return values used in communication

socket() function

#include <sys/socket.h>

int socket(int domain, int type, int protocol);
// The file descriptor is returned if the creation succeeds, and - 1 is returned if the creation fails

domain : Protocol family used in sockets
    PF_INET : IPv4 Internet protocol family
    PF_INET6 :  IPv6 Internet protocol family
    PF_LOCAL :  Local communication UNIX protocol family
    PF_PACKET : Protocol family of underlying socket
    PF_IPX :  IPX Novell protocol family

type : Socket data transmission type information and socket data transmission mode.
    SOCK_STREAM : Connection oriented sockets, TCP
                Reliable, sequential, byte based, connection oriented data transmission socket.
    SOCK_DGRAM :  UDP
                An unreliable, non sequential socket for the purpose of data transmission.

protocol : Protocol information used for communication between computers
    The first two parameters basically determine the protocol type, and the third parameter is generally passed to 0.
    IPPROTO_TCP : TCP
    IPPROTO_UDP : UDP

Note: by default, only 1024 (0-1023) file descriptors can be opened, that is, 1020 sockets can be created. 0, 1 and 2 descriptors represent standard input, output and error by default

Byte order conversion function

#include <arpa/inet.h>

//Host byte sequence to network byte sequence
unsigned short htons(unsigned short);
unsigned long htonl(unsigned long);

//Network byte order to host byte order
unsigned short ntohs(unsigned short);
unsigned long ntohl(unsigned long);

// Complete string conversion and network byte order conversion
in_addr_t inet_addr(const char *string);

// Complete string conversion and network byte order conversion. 1 is returned for success and 0 is returned for failure
int inet_aton(const char * string, struct in_addr *addr);

// Convert 32-bit integer ip address to string
char *inet_ntoa(struct in_addr adr);

Byte order: byte order refers to the order in which data of one byte type occupying a large amount of memory is stored in memory. (small end / large end)

Because different hosts have different CPU s, they may use different byte order to store data. If the byte order of the client and the server is different, the parsed data is also different. Therefore, it is necessary to specify a unified byte order for data transmission. This unified byte order is called network byte order, which generally adopts large end byte order.

listen() function

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//listen() sets sockfd to listen, and allows at most backlog clients to be connected,
//Ignore if more connection requests are received. listen() returns 0 for success and - 1 for failure.

int listen(int sockfd, int backlog);
    sockfd:socket File descriptor
    backlog:stay Linux In the system, it means that the link has been established and waiting accetp()The length of the request queue

accept() and connect() functions

#include<sys/socket.h>
//For client
//The TCP client uses the connect function to initiate a connection request with the TCP server
//Return: 0 if successful, and - 1 if wrong

int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)
    sockfd: socket The socket descriptor returned by the function,
    servaddr: Just want the pointer to the socket address structure
    addrlen: The size of the address structure.
//Note: the socket address structure must contain the IP address and port number of the server.
#include <sys/types.h>
#include <sys/socket.h>
//For server
//Extract the first connection request in the waiting connection queue of the listening socket, create a new socket, and return the file descriptor pointing to the socket. In case of error, return - 1
//Note: the socket used by the client and server for communication is the socket returned by the accept() function
//Previously used socket s can only be used to establish TCP links between clients and servers

int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
    sockfd: Using system call socket()Created socket descriptor
    addr: point struct sockaddr The structure uses the address of the peer socket of the communication layer server(Generally, it is the client address)Fill in.
    addrlen: A value result parameter that the calling function must initialize to contain addr The value of the structure size pointed to. When the function returns, the peer address is included(Generally server address)Actual value of; The general setting is sizeof(struct sockaddr_in)    

If there is no waiting connection in the queue and the socket is not marked as non blocking, accept() blocks the calling function until the connection request appears.
If the socket is marked as non blocking and there is no waiting connection in the queue, accept() returns the error EAGAIN or EWOULDBLOCK.
Generally speaking, accept() is a blocking function when implemented. When the listening socket calls accept(), it gets its own receive first_ Check whether there is a connection packet in buf; If yes, copy out the data, delete the received data packet, create a new socket and establish a connection with the address sent by the customer. If not, block and wait;

send() and recv() functions

#include <sys/types.h>  
#include <sys/socket.h>
//It is used to transfer data from the specified socket to the other host
//Return value: if successful, the actual number of characters transmitted will be returned; if failed, the return value will be - 1 The error reason is stored in errno

ssize_t send(int socket, const void * buf, int len, unsigned int falgs);
    socket:For connected socket. 
    buf:Pointer to send data
    len:The length of data to be sent is not necessarily the same as buf They are the same size
    flags:Generally set to 0, Other values are defined as follows:
	 		MSG_OOB Data transmitted in out-of-band Send out.
	 	 	MSG_DONTROUTE Cancel routing table query
	  	    MSG_DONTWAIT Set to non blocking operation
	        MSG_NOSIGNAL This action doesn't want to be SIGPIPE Signal interruption.

#include <sys/types.h>  
#include <sys/socket.h>
//recv function is used to receive the data sent from the opposite socket.
//Function returns the number of characters received. Return - 1 in case of error, and the value of errno will not be set in case of failure.
//If the opposite end of the socket does not send data, the recv function will block

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    sockfd: For connected socket. 
    buf: Is the memory address used to receive data, which can be C The address of the language basic data type variable can also be array, structure or string, as long as it is a piece of memory.
    len: The length of data to be received cannot exceed buf Otherwise, the memory overflows.
    flags: Fill 0, Other values are of little significance.

matters needing attention

Because TCP sends data in the form of data flow, if it sends multiple data at the same time in a short time, it is possible to splice the multiple data into one data for transmission. This problem is called packet sticking. For example, the sender sends two strings "hello" and "world", but the receiver receives "Hello world" at one time.
At the same time, if the data sent is too long, it may be sent multiple times. This problem is called subcontracting. For example, the sending string "helloworld" is sent, but the receiving party receives two strings "hello" and "world".
However, the TCP protocol guarantees:
1. The order received by the receiver remains the same
2. In the process of sending the subcontracted data, no other data will be inserted between them

Topics: C network computer networks TCP/IP