一、本篇重点
1. 认识IP地址、端口号、网络字节序等网络编程中的基本概念
2. 学习Socket api的基本用法
3. 能够实现一个简单的udp客户端/服务器
二、基本概念
1. 理解源IP地址和目的IP地址
简单的理解,IP地址是用于标识一台机器的,我们通过IP地址去找到我们需要通信的机器,源IP地址就是一个数据包的源头机器的IP地址,目的IP地址就是该数据包要传送到的机器IP地址。
2. 端口号
2.1 什么是端口号?
我们通过IP地址去标识一台具体的机器,但是一个机器中有着许多的进程,我们最终是要将信息交给某个对应的目标进程的,例如我微信发的消息,最终也是要在对方机器中找到对方微信的进程,并将信息交给该进程,所以在网络通信中,还需要对进程进行标识,端口号就是标识唯一进程的
2.2 为什么不能用进程ID去代替端口号呢?
进程ID也可以作为标识一个进程的唯一标识符,但为了达到解耦合的设计,我们的网络系统不能去以来进程管理系统,而是要单独独立的去设计,这是为了方便维护,所以单独设计了端口号去标识唯一的进程。
2.3 端口号的性质
a. 端口号是一个2字节16位的整数
b. 一个端口号只能被一个进程占用,但一个进程可以有多个端口号
c. 在网络通信中,一个进程根据IP地址和端口号,可以找到唯一的一个目的主机中的一个唯一进程,所以网络通信的本质,也可以认为就是进程间的通信
3. TCP协议和UDP协议
3.1 什么是TCP协议和UDP协议?
TCP协议和UDP协议是传输层的协议,它们的任务是为了在不同主机上的应用程序之间提供可靠或高效的数据传输服务。
两种协议各有其应用场景,这里的可靠和不可靠只认为是一种特性,而不分好坏!
3.2 TCP协议(传输控制协议)
a. TCP协议具有可靠的传输特性,存在着确认机制,发送方发送数据后,接收方会返回确认信息,确保数据被正确的接收,如果接受方一定时间内没有确认收到,则会重新传送。
b. 还会通过对数据进行编号,确保数据按顺序到达接收方,并且接收方可以识别丢失或者重复的数据
c. 流量控制,防止发送方发送数据过快,导致接收方来不及处理而出现数据丢失或者缓冲区溢出,接收方通过通知发送方自己接收窗口的大小,控制发送方的发送速度
d. 拥塞控制,当网络拥塞时,TCP会自动调整发送数据的速度,避免加重网络负担,保证网络的稳定性
e. 有连接、面向字节流的服务
3.3 UDP协议
a. 提供高效的数据传输,无连接、不可靠、面向数据报的服务
b. 相比于TCP协议,UDP协议注重的是高效性,由于没有复杂的确认和重传机制,UDP可以快速的发送数据,减少传输延迟
4. 网络字节序
4.1 什么是网络序?
网络字节序是一种计算机网络中规定的字节顺序标准
计算机储存数据时分大端和小端,所谓小端就是低权位的数据放在对应低地址中,反之则是大端,在网络通信中,不同的机器大小端是不确定的,也没办法将源机器是什么端这个信息放在报头去告诉目的机器,因为报头本身也是数据信息。
所以在网络通讯中,我们对大小端进行了统一,但凡发送到网络中的信息,都以大端的方式去发送和读取,这就是网络字节序定义的标准
4.2 网络字节序的相关接口
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);//将源主机中32位长整形转换成网络字节序要求的大端格式
uint16_t htons(uint16_t hostshort);//将源主机中16位短整形转换成网络字节序要求的大端格式
uint32_t ntohl(uint32_t netlong);//将网络中32位长整形(大端)转换成当前主机的格式
uint16_t ntohs(uint16_t netshort);//将网络中16位短整形(大端)转换成当前主机的格式
三、Socket编程
1. 什么是Socket编程?
Socket编程,即套接字编程是一种实现网络通信的编程方式,根据网络协议分层来看,Socket编程是程序员在利用系统调用接口去访问传输层的部分去进行网络通信,其中创建出来的Socket变量可以认为它是一个能够去沟通网络的文件,我们将主机ip和端口号通过它提供的接口与该网络文件进行关联,然后利用接口可以从中去读取或者写入信息,并且利用它去向指定的网络中其他主机的某个进程发消息,只要知道IP地址和端口号就可以,当然还有各种处理连接错误、数据丢失、网络阻塞等问题以及解决方案,这里我们先简单通过一个UDP的小demo去一个个认识相关的接口和参数。
2. 简单实现UDP
关于Socket的接口,我打算通过一个简单的udp模拟实现的小demo去边写边学习和整理,最后再进行一个整合和总结,这也是我在学习和熟悉这个接口的一种思路和方式。
2.1 大体的框架和功能
我们希望模拟实现一个基于UDP协议的一个服务器,模拟client端(客户端)和server端(服务端)相互跨网络通信的功能,为了便于测试,我们先简单的要求,client端向server端去发送一个字符串类型的消息,然后server端接受到后打印信息,并原封不动的把信息发送回给client端,并打印。
基于此设计,我们先将代码的基本框架搭起来:
我们先建立好makefile、udp_client.cc、udp_server.cc、udp_client.hpp、udp_server.hpp文件,并且搭好基本框架,完成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_client udp_server
2.2 udp_server.hpp的设计
(1)基本接口框架(初步)
我们先写服务端,采用面向对象的设计思路先搭建一个简单的框架, 首先是设计服务器的类成员参数,我们初步认为,至少需要一个套接字变量,该服务器的端口号,所在主机的IP信息,并且要有构造和析构,作为一个服务器,对外提供的接口,这里我们简单设计一个初始化的接口(Init)和启动服务器(Start)接口。
#pragma once
#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
namespace chk
{
class UdpServer
{
public:
UdpServer() //对成员变量完成初始化
{}
void Init() //创建出套接字,并绑定端口号和ip
{}
void Start()//时刻读取套接字中的信息数据,并将其返回