Article directory
- 1. Understanding of network communication
- 2. Can the process PID replace the port number?
- 3. Understand the TCP protocol
- 4. Understand the UDP protocol
- 5. socket programming interface
-
- Code analysis of udp_server.hpp
- Code analysis of udp_client.cc
- full code
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
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
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
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
Set the family (16-bit address type) corresponding to local as network communication
Set a private port number port_
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
3. Cloud server, or a server, generally do not specify a certain IP
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
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
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_;//端口号
};
}