Network socket programming: client

Catalog

The following is the basic process of network socket communication:

socket client code:

#include <stdio.h>          
#include <sys/types.h>          
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <getopt.h>


#define  MSG_NUM  1024                               
#define  MSG_STR  "This is a network socket program"

void print_usage(char *progname)
{
        printf("%s usage: \n", progname);
        printf("-i(--SERVER_IP): sepcify server IP address\n");
        printf("-p(--SERVER_PORT): sepcify server port.\n");
        printf("-h(--Help): print this help information.\n");
        return ;
}



int main(int argc,char **argv)
{
      int    sockfd = -1;
      int    rv  =-1;
      char   *SERVER_IP = NULL;
      int    SERVER_PORT = 0;
      int    rw;
      char   buf[MSG_NUM];
      struct sockaddr_in     servaddr;
      struct option longopts[] = {
      {"help", no_argument, NULL, 'h'},
      {"SERVER  IP", required_argument, NULL, 'P'},
      {"SERVER  PORT", required_argument, NULL, 'P'},
      {0, 0, 0, 0}
      };
      while( (rw=getopt_long(argc, argv, "i:p:h", longopts, NULL)) != -1 ) 
            {
                switch(rw)
                {
                    case 'i':
                        SERVER_IP=optarg;
                        break;
                    case 'p':
                        SERVER_PORT=atoi(optarg);
                        break;
                    case 'h':
                        print_usage(argv[0]);
                        return 0;
                }

            }

        if( !SERVER_IP || !SERVER_PORT )
            {
                print_usage(argv[0]);
                return 0;
                
            }
                  
  
      sockfd=socket(AF_INET, SOCK_STREAM,0);
      if(sockfd<0)
      {
         printf("Create socket failure:%s\n",strerror(errno));
         return 0;
      }//Create a socket description word that uniquely identifies a socket. Stored in sockfd, subsequent operations are useful.
      printf("Create socket[%d] successfully!\n",sockfd);
      
      memset(&servaddr, 0, sizeof(servaddr));  ;
      servaddr.sin_family=AF_INET;             
      servaddr.sin_port = htons(SERVER_PORT);    
      inet_aton(SERVER_IP, &servaddr.sin_addr);

      rv=connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
      if(rv<0)
      {
         printf("To socket[%s:%d] failure:%s\n ",SERVER_IP,SERVER_PORT,strerror(errno));
         return -1;
         goto cleanup; 
      }
      printf("To connect socket[%d] successfully!",sockfd);



      rv=write(sockfd, MSG_STR, sizeof(MSG_STR));
      if(rv<0)
      {
          printf("Write data to socket[sockfd] failure:%s\n",strerror(errno));
          return -2;
         goto cleanup; 
      }
      printf("Write data to socket[%d] successfully!\n",sockfd);
     while(1)
     {
         memset(buf,0,sizeof(buf));
         rv=read(sockfd,buf,sizeof(buf));
      if(rv<0)
      {
          printf("Read data from socket[%d] failure:%s\n",sockfd,strerror(errno));
          return -3;
         goto cleanup; 

      }
       else if (rv==0)
      {
          printf("The socket[%d] has disconnected!",sockfd);
          return -4;
         goto cleanup; 
      }
    
      printf("Read %d bytes data from socket[%d]:%s\n",rv,sockfd,buf);
     }
cleanup: 
close(sockfd);
     return 0; 

}

socket operation API function

socket provides function interfaces corresponding to various operations. Here are some parameters used in the above code.
----------------------

socket() function

int socket(int domain, int type, int protocol);

Socket function corresponds to the open operation of ordinary file. The open operation of a normal file returns a file description word, while socket() is used to create a socket descriptor, which uniquely identifies a socket. The socket description word is the same as the file description word, and it is useful for subsequent operations. Take it as a parameter to perform some read and write operations. When creating a socket, you can also specify different parameters to create different socket descriptors. The three parameters of the socket function are:
◆ domain: that is, IP address type, commonly used are AF ﹣ INET and AF ﹣ inet6. AF is short for "Address Family" and INET is short for "Inetnet". AF < INET = IPv4 address, for example 127.0.0.1; AF < inet6 = IPv6 address, for example 1030::C9B4:FF12:48AA:1A2B.
◆ type: Specifies the socket type. The commonly used socket types are sock ﹣ stream, sock ﹣ Dgram, sock ﹣ raw, sock ﹣ packet, sock ﹣ seqpacket, etc.).
◆ protocol: Protocol represents the transmission protocol, commonly used are ipproto? TCP and ipptoto? UDP, respectively representing the TCP transmission protocol and UDP transmission protocol. When the protocol is 0, the default protocol corresponding to type will be automatically selected.

----------------------

connect() function

After the TCP client program calls socket() to create socket fd, it can call the connect() function to connect to the server. If the client calls connect() at this time to send out the connection request, the server will receive the request and make accept() return. The new file descriptor returned by accept() is the TCP connection corresponding to the client. Through these two file descriptors (fd of the client connect and fd of the server accept), the communication between the client and the server can be realized.

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

◆ sockfd: description word created by socket() of client
◆ addr: socket address information of the server to be connected, including IP address, port and other information of the server
◆ addrlen: the length of socket address

Before calling connect, we need to set the IP address and port of the server to addr.

memset(&servaddr, 0, sizeof(servaddr));  ;
      servaddr.sin_family=AF_INET;             
      servaddr.sin_port = htons(SERVER_PORT);    
      inet_aton(SERVER_IP, &servaddr.sin_addr);
      rv=connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
      if(rv<0)
      {
         printf("To socket[%s:%d] failure:%s\n ",SERVER_IP,SERVER_PORT,strerror(errno));
         return -1;
         goto cleanup; 
      }
      printf("To connect socket[%d] successfully!",sockfd);

----------------------

read(), write(), etc

After the client connects with the server through connect() and the server establishes the TCP socket link through accept(), the network I/O function can be called for read-write operation, that is, the network communication is realized. For specific usage, please refer to Baidu or my previous blog: https://blog.csdn.net/weixin_46378291/article/details/104771835

----------------------

close(), shutdown() functions

After the connection between the server and the client is established, some read-write operations will be carried out. After the read-write operation is completed, the corresponding socket description word will be closed, just like the open file after the operation calls close() to close. Close the default behavior of a TCP socket is to mark the socket as closed and return it to the calling process immediately. The descriptor can no longer be used by the calling process, that is, it can no longer be used as the first parameter of read or write.

int close(int fd);

If close() is called for socket fd, the four-way handshake of the TCP connection disconnection will be triggered. Sometimes, we need to send data to the other side and then close the socket. Then we can call the shutdown() function to half close the socket:

int shutdown(int sockfd, int how);

If the value of how is shut'rd, the socket can no longer read data; if the value of how is shut'wr, the socket can no longer send data; if the value of how is shut'rdwr, the socket can neither read nor write data.

----------------------

inet_pton() and inet_ntop() functions

In the previous code, we used inet_aton() or inet_ntoa() to complete the conversion between IPv4 dotted decimal string and 32-bit integer data.

 inet_aton(SERVER_IP, &servaddr.sin_addr);

inet_aton() transforms the IP address of the network host (such as 192.168.1.10) into binary value, and stores it in the struct in_addr structure, that is, the second parameter * inp. If the function returns non-0, it means that the cp host is valid, and if it returns 0, it means that the host address is invalid. But these two functions are only suitable for IPv4 addresses. The following two functions are compatible with both IPv4 and IPv6 addresses:

int inet_pton(int family, const char *strptr, void *addrptr);
//Convert the dotted decimal ip address into a numeric format for network transmission, return value: 1 if successful, 0 if the input is not a valid expression, and - 1 if there is an error
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
//Convert the numeric format to dotted decimal ip address format, return value: pointer to structure if successful, NULL if error

----------------------

Network byte order and host byte order

Host byte order: This is what we usually call the big end and small end mode: different CPU s have different byte order types. These byte orders refer to the order in which integers are saved in memory. This is called host order.
Network byte order: the 32 bit values of 4 bytes are transmitted in the following order: first 0-7 bit, then 8-15 bit, then 16-23 bit, finally 24-31 bit. This order of transmission is called big endian. Because all binary integers in the TCP/IP header are required to be in this order when they are transmitted in the network, it is also called network byte order.
Therefore, when binding an address to a socket, please first convert the host byte order to the network byte order, instead of assuming that the host byte order uses big endian like the network byte order. The functions htons() and htoll () are used to convert port and IP address into network byte order, respectively. Refer to the code example above.

 servaddr.sin_port = htons(SERVER_PORT);
 serv_addr.sin_addr.s_addr = htonl(192.168.1.141);

Here, two functions htons() and htolnl() are called to convert port and IP address into network byte order. h in the names of these two functions represents host, n represents network, s represents short(2 bytes / 16 bits), and l represents long(4 bytes / 32 bits). Because the port number is 16 bits, we use htons() to convert the port number from the host byte order to the network byte order, and the IP address is 32 bits, so we use htonl() function to convert the IP address from the host byte order to the network byte order.

Published 2 original articles, praised 0 and visited 14
Private letter follow

Tags: socket network

Posted on Tue, 17 Mar 2020 04:25:25 -0400 by Nukeum66