【Computer network】socket programming

1. Understanding of network communication

When host A hands over its own data to host B, it needs to send a message to host B, and host B will send a message back to host A in the future

But in fact, it is not the ultimate goal for host A to hand over its own data to host B

For example: you bought a piece of clothing on Taobao, and after the seller delivered the goods, the goods were shipped from Guangdong Province to your area, and finally the package successfully arrived in your hands, you still need to decide how to use the express

The transmission of data is not the purpose, but the purpose is to allow two hosts to communicate through data to complete the task cooperatively


For example: Tang Seng said he was going to the West to learn Buddhist scriptures. The corresponding temple of Tang Seng is the host of A, and the Daleiyin Temple in Xitian is the host of B. Tang Seng is not finished with the big class of food, this is just his means, he still needs to
meet Tathagata, Tathagata will provide him with scripture services


When the data is initiated, it starts from the transport layer of host A
and is handed over to the transport layer of host B. The data is transmitted from some kind of client in the application layer of host A,
and the transfer of data to the transport layer of host B is not the direct purpose. , to hand over the data to some kind of server in the application layer

The client corresponding to host A must be started, so its essence is the process

Because some kind of server on host B is running as a process, you can access some kind of service anytime and anywhere

The essence of network communication is inter-process communication


The first stage of communication: first send the data to the target host (means) through the operating system. The
second stage of communication: the host will push the received data to the specified process on its upper layer

The first stage can be completed through the TCP/IP protocol, because IP can represent the only host on the Internet

When the transport layer of host B hands over the data to the application layer, there are many processes corresponding to the application layer.
Therefore, in order to identify the uniqueness of the network process on the host, the concept of port number is proposed.

The port number is a field of the transport layer protocol, which is a 2-byte 16-bit integer used to identify the uniqueness of the process at the system level

So the IP address + port number can represent the only process in the Internet

When communicating, there are two processes for communication, so there are source IP and source port number and destination IP and destination port number. The
source IP and source port number represent the only process in the Internet.
The destination IP and destination port number also represent the only one process

Therefore, the essence of network communication is to construct uniqueness through IP+PORT number to communicate between network processes, referred to as socket communication

2. Can the process PID replace the port number?

The process PID is also unique for each process at the system level, and can also represent the uniqueness of the process on the system, so the process PID can be used instead of the port number, but there will be some problems. 1. Not all processes need to perform network
communication
. Only some processes may communicate with the network. If the process PID is used as the network to identify the process, it is difficult to distinguish clearly which is for network communication and which is not for network communication.

2. PID is the concept of operating system process management. The network module also includes the process management part. Otherwise, the inability to recognize PID
will increase the coupling degree of process management and network management in the system.

3. Understand the TCP protocol

TCP protocol (Transmission Control Protocol) Transmission Control Protocol
Features:
The transport layer protocol
is connection-oriented.
During the communication process, it will have its own reliability. It is
byte-oriented and stream-oriented
. When sending and receiving data, there is no concept of messages at the TCP layer. A bunch of data, give this pile of things to the upper application layer at a time, or hand over byte by byte.
TCP doesn’t care about how to explain the data, it only cares about how much you want and how much it will give you, and finally explain the information Interpreted by the application layer itself, this slave is called byte stream

4. Understand the UDP protocol

UDP (User Datagram Protocol) User Datagram Protocol
Features:
transport layer protocol ,
connectionless and
unreliable transmission
for datagrams
such as: receiving express delivery, receiving one is a complete express delivery, and it is impossible to receive half or half of a specific express delivery. If the other party sends three times, you must receive three times

5. socket programming interface

The laboratory has developed a set of inter-process communication standards, which can not only communicate locally, but also communicate across hosts in the network, that is, the socket standard belongs to the posix standard

The most common socket sockaddr_in based on network communication

Pre-inter-socket (using local process communication between two processes) sockaddr_un

The designer of the socket designed a public data structure called struct sockaddr in order to allow everyone to communicate locally and network through a set of interfaces. If you want to communicate over the network (struct sockaddr_in) or communicate locally (struct sockaddr_un) , just use sockaddr for mandatory conversion


At the very beginning of the structure, there must be 16-bit address types
AF_INET and AF_UNIX are actually macros, represented by integers. Compare and
judge the address.
If it is equal to AF_INET, it is for network communication. Forcibly convert sockaddr to sockaddr_in
. Equal to AF_UNIX, for local communication, force sockaddr to sockaddr_un


Code analysis of udp_server.hpp

Through the communication function of the network protocol stack, the data is delivered to the application layer of the other party to complete the communication between the two processes

To hand over the client's data to the server, you need to send a message to the server, and the server will return a message to the client


Use namspace in udp_server.hpp to name the namespace ns_server
and define a class udpserver

socket - create a socket file descriptor

input man socket, create socket

The first parameter domain is used to distinguish between network communication and local communication.
If you want to communicate over the network, use AF_INET
; if you want to communicate locally, use AF_UNIX

The second parameter type, the service type corresponding to the socket

insert image description here

SOCK_STREAM stream socket
SOCK_DGRAM connectionless unreliable communication (user datagram)

The third parameter, protocol, indicates which protocol you want to use. The default protocol is 0.
If it is a stream socket, the system will consider it to be the TCP protocol. If it is a user datagram, the system will consider it to be the UDP protocol.

The return value of the socket: the file descriptor is returned if successful, and -1 is returned if it fails

Initserver - initialization

1. Create a socket interface and open a network file

insert image description here
Use socket sockets to create network communication and UDP protocols.
If the socket returns -1 to indicate failure, the initialization will also fail, and there is no need for the program to continue running, so use exit to terminate the program

If the socket is successfully created, the file descriptor will be returned.
The first three file descriptors are occupied by standard input, standard output, and standard error, so the file descriptor at this time should print 3

bind - the use of binding

Enter man 2 bind, view binding

Bind a name to a socket
The first parameter sockfd is the file descriptor
The second parameter addr is the general structure type
The third parameter addrlen is the actual length of the second parameter

bind return value: if successful, return 0, if failed, return -1

2. Specify the IP address and port number for the server

If you want to use the struct sockaddr_in type, you need to add a header file

Define a variable local of type struct sockaddr_in (network communication)

Understanding of struct sockaddr_in

insert image description here
Transfer struct sockaddr_in to define
16-bit address type: replace sa_prefix with sin_, sin## family is actually sin_family
at this time, sin_port corresponds to the currently bound port number
sin_addr corresponds to IP address
again transfer in_addr to definition, IP The address is a 32-bit integer

bzero empty

sin_zero is used as the filling field of the structure.
The structure may be too large to be used up, so just use the filling field to fill it

enterman bzero

There will be a buffer of n bytes, all written as 0

Code

insert image description here

Set the family (16-bit address type) corresponding to local as network communication


Set a private port number port_


insert image description here
Set a port number outside the class for construction, if no port number is passed in, 8082 acts as the default value


If I send you a message, I need to send the message back in the future, so I must know my IP address and port number,
that is, the port number is sent to the network in the form of a message

The port_ defined in the class is called the local host sequence, and this port_ needs to be converted from the host sequence to the network sequence

Enter man htons, which represents the host-to-network sequence of short integers



Define a private variable ip_ Since the IP address we set is in string style, and the IP address in the system is in 4-byte style, it is necessary to convert the string style to 4-byte style
inet_addr - Convert string style to 4-byte style

enterman inet_addr

The function is: convert the string-style IP address into a 4-byte style IP address, and convert the host sequence to the network sequence by default


Since local is actually defined on the stack of the user layer, it is not in the kernel

So with the help of bind, the filled socket field and the file field are bound and associated, and such a file is a network file.
Since local is of type struct sock_addr_in, it needs to be converted to the public type of struct sockaddr


The server specifies its own IP address

At this time, run the udp_server executable program, and you will find that the socket is created successfully, but the binding will fail


The cloud server does not need to bind the IP address, you need to let the server specify the IP address itself


So add command line parameters to the main function Command line parameters
The
two parameters of the main function, char* argv[] is an array of pointers, argv is a table containing pointers, the pointer points to the string
int argc, and argc is an array number of elements

Design a usage function to indicate the name of the executable program in question proc


Create an err.hpp again, use enum enumeration, set USAGE_ERR to 1, set SOCKET_ERR (socket error) to 2 by default, and
set BIND_ERR (binding error) to 3


Use the second subscript of the argv array to specify the port number of the string style, and then use atoi to convert the string into an integer, and
finally only pass in the port number


insert image description here

3. Cloud server, or a server, generally do not specify a certain IP

insert image description here

Use INADDDR_ANY to let udpserver bind any IP on this host when it starts


Turn INADDDR_ANY to the definition, which is actually the default value of 0


start - start

The essence of the server is an endless loop, and it will never exit.
For example, if you open the King of Glory in the middle of the night, you can still play


1. Receive a message from the client

recvfrom - get user datagram

Enter man recvfrom to get user datagram

The first parameter sockfd is the socket.
The second parameter buf is the buffer defined by itself.
The third parameter len is the length of the buffer.
The fourth parameter flags is the read mode, which is set to 0 by default and read in blocking mode
The remaining two parameters src_addr and addrlen are input and output parameters.
Use recvfrom to receive data, and finally return the data. If you want to return the data, you must know who the other person is.
src_addr is a structure that internally records the client’s IP address and The port number
addrlen is the size of the output structure
Return value: If it is greater than 0, the read is successful


Define a struct sockaddr_in (network communication) variable peer
to use len to represent the future structure size

If n is greater than 0, the read is successful, and the next position of the last position is set to \0;
if the read fails, continue to read


The IP address under the peer is a 4-byte integer, which needs to be converted into a string style

inet_addr - Convert 4-byte style to string style

Input man inet_addr, convert 4-byte IP to string-style IP


The port number under peer is the network sequence. If you want to get the client port number clientport, you need to use ntohs to convert the network sequence to the host sequence

2. Send the message to others

sendto

enterman sendto

The first parameter sockfd is the socket.
The second parameter buf is the buffer defined by itself.
The third parameter len is the length of the buffer.
The fourth parameter flags is the read mode, which is set to 0 by default and read in blocking mode
The remaining two parameters src_addr and addrlen are input and output parameters.
Use recvfrom to receive data, and finally return the data. If you want to return it, you must know who the other person is.
src_addr is to transfer the previously received message to the client
addrlen It is the size of the structure when output
Return value: If it is greater than 0, the read is successful



Code analysis of udp_client.cc

The first parameter is used AF_INET, which means network communication.
The second parameter is used SOCK_DRAM, which means datagram.
The third parameter is set by default 0. Since the above is datagram, it is UDP protocol.


How is the client bound?

The client is
a socket that needs to be bound. The essence of communication is the network version of the inter-process communication between the client's IP and port number and the server's IP and port number. However, the client does not need to
bind itself, and the operating system automatically binds it.
For example, computers and mobile phones are filled with a large number of clients. These clients come from different companies. The port number of each client cannot be fixed and must be randomly selected by the operating system. The essence is to prevent certain
clients from being occupied by others. , to reduce conflicts at the client level
, so the port number of the client should be randomly assigned by the operating system to prevent startup conflicts on the client

Why should the server bind itself?

1. The port of the server is well known and cannot be changed arbitrarily.
For example: 110 is the alarm number. It is impossible to change the alarm number every day, otherwise it will cause people not to know which one to call when they really want to make a call.

2. The servers are all owned by the same company, so the port numbers need to be unified and standardized.
For example: Taobao will not deploy its services to Zhihu


Code

Perform a while loop to send a message to the server

There is currently no message, so let the user input act as the source of the message
Use sendto to send the message to the server

Send messages to the server host as a client If
you want to run the client, you need the IP and port number of the server


insert image description here
With the help of command line parameters, the second parameter entered by the user is used as the IP of the server, and the
third parameter entered by the user is used as the port number of the server

Although the IP and port number of the server are known at this time, but if you want to use sendto, the last two parameters require a socket structure


Create a new structure server, which contains the IP and port number of the server
Use htons to convert the host sequence to a network sequence
Use inet_addr to convert the string into 4 bytes


insert image description here

At this time, the last two parameters of sendto add the created structure sever to complete the task of sending the server.
Since the type of the server is struct sockaddr_in, and the type of the parameter is the public structure type struct sockaddr, it needs to be forced


Use revfrom to get the user datagram
and receive the message from the server, so define a temp structure for receiving

When the first system call sends data, the operating system randomly selects the port number of the client at the bottom layer plus its own IP to
build the bind first, and then builds the sent data message

full code

err.hpp (enumeration of error codes)

#pragma once


enum 
{
    
    
    USAGE_ERR=1,
    SOCKET_ERR,
    BIND_ERR
};

makefile

.PHONY:all
all: udp_client udp_server

udp_client:udp_client.cc
	g++ -o  $@ $^ -std=c++11
udp_server:udp_server.cc
	g++ -o $@  $^ -std=c++11

.PHONY:clean
clean:
	rm -f udp_clinet udp_server
  

udp_client.cc (client implementation, no encapsulation)

#include"udp_client.hpp"
#include"err.hpp"
#include<cstring>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

static void usage(std::string proc)
{
    
    
    std::cout<<"usage:\n\t"<<proc<<"serverip serverport\n"<< std::endl;
}
// ./udp_client serverip sevrerport
int main(int argc ,char* argv[])//命令行参数 传入的是 客户端的运行 服务器的IP和端口号
{
    
    
  if(argc!=3)
  {
    
    
    std::cout<<" "<<std::endl;
    exit( USAGE_ERR);//终止程序
  }
   std::string serverip = argv[1];//服务器的IP
   uint16_t serverport =atoi(argv[2]);//服务器的端口号

  int sock=socket(AF_INET,SOCK_DGRAM,0);
  if(sock<0)//创建套接字失败
  {
    
    
     std::cout<<"create socket error"<<std::endl;
     exit( SOCKET_ERR);
  }

  //明确server是谁
  struct sockaddr_in server;//设置网络通信的结构体
  memset(&server,0,sizeof(server)); //将结构体清空
  server.sin_family=AF_INET;
  server.sin_port=htons(serverport);//端口号
  server.sin_addr.s_addr=inet_addr(serverip.c_str());//IP地址

  while(true)
  {
    
    
    //用户输入  
    std::string message;
    std::cout<<  "please enter# ";
    std::cin>> message;
    //发送消息
    sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));
   
   //接收消息
    char buffer[1024];
    struct sockaddr_in temp;
    socklen_t len=sizeof(temp);
    int n=recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&len);
    if(n>0)
    {
    
    
      buffer[n]=0;
      //收到回显消息
      std::cout<<"server echo"<<buffer<<std::endl;
    }
  }
    return 0;
}

udp_clinet.hpp

#pragma once
#include<iostream>
using namespace std;

udp_server.cc (with encapsulation)

#include"udp_server.hpp"
#include"err.hpp"
#include<memory>
#include<string>
using namespace ns_server;
using namespace std;

static void usage(string proc)
{
    
    
    std::cout<<"usage:\n\t"<<proc<<"prot\n"<< std::endl;
}

//udp_server port
int main(int argc,char*argv[])//命令行参数
{
    
    
    if(argc!=2)//若命令行参数个数不为2,则当前会报错
    {
    
    
        usage(argv[0]);
        exit(USAGE_ERR);//终止程序
    }
    //端口号
    uint16_t port=atoi(argv[1]);//atoi可将字符串转化为整数

    //只需传入由用户指明的端口号
   unique_ptr<UdpServer> usvr(new UdpServer (port));

   usvr->Initserver();//服务器的初始化
   usvr->Start();//启动服务器
    return 0;
}






udp_server.hpp (server implementation)

#pragma once
#include<iostream>
#include<cerrno>
#include<cstring>
#include<cstdlib> 
#include<strings.h>
#include<functional>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include"err.hpp"

namespace  ns_server
{
    
    
  const static uint16_t  default_port=8082;//设置端口号为8082 
   class UdpServer
   {
    
    
     public:
       UdpServer(uint16_t port=default_port)//构造
       :port_(port)
       {
    
    }

      void  Initserver()//初始化
       {
    
    
        //1.创建套接字接口,打开网络文件
        sock_=socket(AF_INET,SOCK_DGRAM,0);
        if(sock_<0)//创建失败
        {
    
    
          //打印错误信息
            std::cout<<" create socket   error: "<<strerror(errno)<<std::endl;
            exit(SOCKET_ERR);//终止程序
        }
         std::cout<<"create socket success:"<<sock_<<std::endl;//3

         //2.给服务器指明IP地址和端口号
         struct sockaddr_in local;
        bzero(&local,sizeof(local));//全部置为0
        local.sin_family=AF_INET;//将16位地址类型 置为 网络通信
         local.sin_port=  htons(port_); //主机转网络的端口号

         //1.需要将字符串风格转化为 4字节
         //2.需要 将主机序列转换为 网络序列
         local.sin_addr.s_addr= INADDR_ANY ; //bind本机上的任意IP
         
         //bind 绑定
         int n=bind(sock_,(struct sockaddr*)&local,sizeof(local));
         if(n<0)//绑定失败
         {
    
    
           std::cout<<" bind  socket   error: "<<strerror(errno)<<std::endl;
           exit(BIND_ERR);
         }
           std::cout<<"bind socket success:"<<sock_<<std::endl;//3
       }
       void Start()//启动
       {
    
    
        char buffer[1024];//用于保护用户数据
         //设置一个死循环
           while(true)
           {
    
    
            //1.收到客户端发来的消息
             struct sockaddr_in peer;
             socklen_t len=sizeof(peer);//传入的缓冲区大小
             int n=recvfrom(sock_,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
             if(n>0)
             {
    
    
              buffer[n]='\0';
             }
             else 
             {
    
    
              //读取失败,则继续读取
              continue;
             }

             //提取客户端信息
             //4字节IP转为 字符串IP
             std::string clientip =inet_ntoa(peer.sin_addr);//客户端IP
             //将网络序列转换为主机序列
             uint16_t clientport =ntohs(peer.sin_port);//客户端 端口号
             std::cout<<clientip<<"-"<<clientport<<"-"<<"get message# "<<buffer<<std::endl;

             //2.将消息发给别人
             sendto(sock_,buffer,strlen(buffer),0,(struct sockaddr*)&peer,sizeof(peer));
             
           }
       }
       ~UdpServer()//析构
       {
    
    }
     private:
     int sock_; //文件描述符
     uint16_t port_;//端口号 
   }; 
}

Guess you like

Origin blog.csdn.net/qq_62939852/article/details/132090486