Linux Network Programming TCP

TCP/IP protocol

TCP/IP protocol stack is the sum of a series of network protocols. It is the core framework of network communication. It defines how electronic devices connect to the Internet and how data is transmitted between them.

Correspondence between OSI 7-layer model and TCP/IP 4-layer network model

The basic knowledge of computer network is not enough to explain, mainly to let everyone understand which layer the next Linux network programming data flow belongs to, as shown in the figure below

TCP/IP protocol data flow diagram

The Tcp protocol of Linux network programming we will explain next is a protocol belonging to the transport layer

Linux Socket network programming

TCP protocol

TCP is a reliable connection oriented transport layer protocol.

TCP programming

Network programming in Linux is carried out through socket interface. Socket is a special I/O interface and a file descriptor. It is often used for communication between processes on different machines. Of course, it can also realize communication between processes on local machines.

Flow chart of using TCP protocol

Explain API interfaces one by one according to the flow chart

Server API interface

socket

#include <sys/socket.h>
int socket(int family //Protocol cluster general AF_INET PF_INET
 ,int type   //Socket type sock_ Stream (byte stream socket interface)
 ,int protocol);  //Non original socket, parameter 0

Socket type:

- SOCK_ Stream (byte stream socket interface)

- SOCK_ Dgram (datagram socket interface)

- SOCK_ Raw (original socket)

Example:

listenfd = socket(AF_INET,SOCK_STREAM,0);

bind

Assign a local IP and protocol port to the socket

#include <sys/socket.h>
int bind(int socket
, const struct sockaddr *address//Protocol family address
,socklen_t address_len); //Protocol family length  

-Address: protocol family address, general socket address

The general socket address is not easy to use, so Linux provides a special socket address structure for each protocol family

UNIX native protocol family
struct sockaddr_un {
 sa_family_t sa_family;
 char sun_path[100];
}
TCP/IP protocol family

The TCP/IP protocol family has sockaddr_in and sockaddr_in6 two dedicated socket address structures, corresponding to IPv4 and IPv6 respectively

Protocol family SOCKADDR corresponding to IPv4_ In is defined as follows

struct sockaddr_in {
 sa_family_t sin_family; /*Address family: AF_INET*/
 in_port_t sin_port; /*Port number in network byte order*/
 struct in_addr sin_addr; /*ipv4 address*/
}
/* Internet address. */
struct in_addr {
    uint32_t       s_addr;     /* address in network byte order */
};

ipv6 is rarely used, so the definition is not introduced separately

Commonly used are sockaddr_ In (network address), sockaddr_ UN (local address), which is cast to sockaddr * pointer type when passing in parameters. The example is as follows.

struct sockaddr_in servaddr;
/*(2) Set the server protocol family sockaddr_in structure*/
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;//Must be consistent with the creation fimile of the socket
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//Indicates that any IP address is acceptable
servaddr.sin_port = htons(8888);
bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));

listen

The listen function is only called by the TCP server

#include <sys/socket.h>
int listen(int sockfd //Socket description returned by socket function
 ,int backlog);   //This value indicates the queue size and the maximum number of connections when listen ing
listen(listenfd,2);

accept

#include <sys/socket.h>         
int accept(int listenfd   //Socket function returns socket descriptor listener handle
 , struct sockaddr *client //Protocol family address
 , socklen_t * addrlen);  //Client socket
accept(listenfd , (struct sockaddr *)&cliaddr , &clilen);

Used to receive a new connection.

Client API interface

connect

#include <sys/socket.h>      
  int connect(int sockfd
   , const struct sockaddr * addr //Protocol family address
   , socklen_t addrlen);   //Protocol family length  
connect(sockfd , (struct sockaddr *)&servaddr , sizeof(servaddr))

Used to connect to the server

send/recv

#include <sys/types.h>
#include < sys/socket.h >         
ssize_t send(int sockfd, const void *buf, size_t len, int flags); 
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

The first three parameters are the same as read(). The parameter flags is the transmission control flag. UDP will be introduced in detail

TCP case

Server

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>
#include<netdb.h>
#include<arpa/inet.h>
#define MAX_BUFF 1024
#define MAX_LISTEN 10
int main(int argc,char *argv[])
{
  int defaule_port = 8000;
  int optch = 0;
  while((optch = getopt(argc, argv, "s:p:")) != -1)
  {
    switch (optch)
    {
      case 'p':
          defaule_port = atoi(optarg);
          printf("port: %s\n", optarg);
          break;
      case '?':
          printf("Unknown option: %c\n",(char)optopt);    
          break;
      default:
          break;
    }
  }
 /*Declare server address and client link address*/
 struct sockaddr_in server_addr,client_addr;
 socklen_t client_len;

 /*Declare server listening socket and client link SOCKET*/
 int listen_fd,connect_fd;

  /*(1) Initialize listening socket listenfd*/
 listen_fd = socket(AF_INET, SOCK_STREAM,0);
 if(listen_fd == -1)
 {
   perror("Socket Error:");
   return 0;
 }
    
 /*(2) Set up server sockaddr_in structure*/
 bzero(&server_addr,sizeof(server_addr));
 server_addr.sin_family = AF_INET;
 server_addr.sin_addr.s_addr = htonl(INADDR_ANY);    //Any address
 server_addr.sin_port = htons(defaule_port);

 /*(3) Binding sockets and ports*/
 if(bind(listen_fd,(struct sockaddr*)&server_addr,sizeof(server_addr))==-1)
 {
   perror("Bind error:");
   return 0;
 }

 /*(4) Listen to customer requests*/
 if(listen(listen_fd,MAX_LISTEN)==-1)
 {
   perror("Listen error:");
   return 0;
 }
 
 /*(5) Accept customer requests*/
 for(;;)
 {
   client_len = sizeof(client_addr);
   connect_fd = accept(listen_fd,(struct sockaddr*)&client_addr,&client_len);
   if(connect_fd < 0)
   {
     perror("accept error");
     return 0;
   }

   printf("Connect from %s:%u...\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
   /*Declare the buffer and send data to the client*/
   char buff[MAX_BUFF] = "hello\n";
   if(-1 == write(connect_fd,buff,strlen(buff)))
   {
     perror("Send error\n");
     return 0;
   }
   printf("Send success...\n");
   /*Empty the buffer and block the data sent by the client waiting to be read*/
   memset(buff,'\0',sizeof(buff));
   if(-1 == read(connect_fd,buff,MAX_BUFF))
   {
     perror("read error\n");
     return 0;
   }
   write(1,buff,strlen(buff));
   close(connect_fd);
  }
  close(listen_fd);
  return 0;
}

Compile and run. The default port is 8000, - p specify the port

gcc -o server.c server
./server -p 8020

client

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
const int MAX_LINE = 2048;
int main(int argc ,char**argv)
{
    /*Declare socket and linked server addresses*/
  int sockfd;
  int optch,ret = -1;
  const char*server_addr;
  int default_port = 8000;

  struct sockaddr_in servaddr;
  /*To judge whether it is a legal input, you must pass in a parameter: server Ip*/
  if(argc<3)
  {
    printf("usage:tcpcli <IPaddress>");
    return 0;
  }
  while((optch = getopt(argc, argv, "s:p:")) != -1)
  {
    switch (optch)
    {
        case 's':
            server_addr = optarg;
            break;
        case 'p':
            default_port = atoi(optarg);
            printf("port: %s\n", optarg);
            break;
        case '?':
            printf("Unknown option: %c\n",(char)optopt);    
            break;
        default:
            break;
    }
  }
  /*(1) Create socket*/
  sockfd =socket(AF_INET,SOCK_STREAM,0);
  if(sockfd==-1)
  {
      perror("socket error");
      return 0;
  }
  /*(2) Set up linked server address structure*/
  bzero(&servaddr,sizeof(servaddr));
  servaddr.sin_family =AF_INET;
  servaddr.sin_port = htons(default_port);
  if(inet_pton(AF_INET , server_addr , &servaddr.sin_addr) < 0)
  {
    printf("inet_pton error for %s\n",server_addr);
    return 0;
  }
  /*  (3) Send link server request  */
  if( (ret = connect(sockfd , (struct sockaddr *)&servaddr , sizeof(servaddr))) < 0)
  {
    perror("connect error");
    return 0;
  }
  printf("connect seccess,ret:%d..\n",ret);
  struct sockaddr_in c_addr;
  memset(&c_addr, 0, sizeof(c_addr));
  socklen_t len = sizeof(c_addr);

  char buf[MAX_LINE];
  while (1)
  {
      /* code */
      memset(buf,'\0',sizeof(buf));
      len = read(sockfd,buf,sizeof(buf)-1);
      if(len == 0)
      {
          printf("server close..\n");
          return 0;
      }
      printf("recv from server:%s",buf);

      memset(buf,'\0',sizeof(buf));
      printf("please enter:\n");
      ssize_t len = read(0,buf,sizeof(buf)-1);
      if(len>0)
      {
          if(strcmp(buf,"quit")==0)
          {
              printf("quit\n");
              break;
          }
          buf[len - 1]='\0';
          write(sockfd,buf,strlen(buf));
      }
  }
  close(sockfd);
  return 0;
}

Compile and run. The default port is 8000, - s specifies the connected server, - p specifies the port

$ gcc -o client client.c
$ ./client -s 0.0.0.0 -p 8020
connect seccess,ret:0..
recv from server:hello
please enter:
hello too
server close..

That's all for the simple tcp server and client. The next issue introduces multithreading technology to realize a multithreaded chat room program.

Added by thinguy on Tue, 16 Nov 2021 11:24:26 +0200