基于C语言的计算机网络编程(初识)
1.计算机网络概述
1.最早的广域网
在通信双方或多方之间,通过电路交换建立电路连接的网络。

2.电路交换网的特点
建立链接->使用链接->释放链接
物理通路被双方独占。
计算机数据是突发式出现在数据链路上的,而电路交换网的建立链接、使用链接、释放链接三个过程使得传输效率太低,故电路交换不适合传输计算机数据。
3.计算机的网络要求
现在我们国家的网络基本由移动、联通、电信,来进行下分,说白了国家把网络的一些布置,和网络的下分交给了这三大机构来实现。
8.多级结构因特网
NSFNET逐步商用因特网主干网代替

9.TCP/IP协议简介
为了使各种不同计算机之间进可以互联,ARPANET指定了一套计算机通信协议,即TCP/IP协议(族)。
为了减少协议设计的复杂性,大多数网络模型均采用分层的方式来组织 ,每一层利用下一层提供的服务来为上一层提供服务,本层服务的实现细节对上层屏蔽。
10.分层结构
为了实现不同类型的计算机和不同类型的操作系统之间的通信,引入了分层的概念。
最早的分层体系结构叫做OSI开放系统互联模型,是由国际化标准组织(ISO)指定的,由于OSI过于复杂,所以到现在为止也没有使用它,而使用的是TCP/IP协议族。
OSI一共分为7层,TPC/IP协议族一共分为4层,虽然TPC/IP协议族层次少,但是却干了OSI7层所有任务

应用层:应用程序间沟通的层(手机电脑上用的微信QQ,用户能操纵的全都是应用层的)
例如:FPT(文件传输协议)、Telnet、HTTP (超文本协议)等。
传输层:提供进程之间的数据传送服务
负责传送数据、提供应用程序端到端的逻辑通信
例如:TCP、UDP。
网络层:提供基本的数据封包传送功能
最大可能的让每个数据包都能到大目的主机
例如:IP(网际协议)、ICMP等。
链路层:负责数据帧的发送和接收
每层完成自己的任务,最终通过不同层次的处理完成数据的收发。

(U形 从我传输数据到张3)
11.IP协议简介
IP协议也叫做网际协议。
特指为实现在一个相互连接的网络系统上从源地址到目的地址传输数据包(互联网数据包)所提供必要功能的协议。
特点:
不可靠:它不能保证IP数据包能成功到达它的目的地,仅提供尽力而为的传输服务。
(就告诉你对方是谁,至于你怎么传输,它不管。)
无连接:IP并不维护任何关于后续数据包的状态信息。每个数据包的处理是相互独立的。IP数据包可以不按发送顺序接收。
IP数据包中含有发送它主机的IP地址(源地址)和接收它主机的地址(目的地址)。
在网络中IP地址是我们主机的唯一标识。
12.TPC协议简介
TPC协议——传输控制协议。
TPC是一种面向连接的,可靠的传输层通信协议。
功能:
提供不同主机上 的进程间通信。
特点:
建立链接->使用链接->释放链接(虚电路)。
TPC数据包中包含序号和确认序号。(保证数据传输的无误)
对包进行排序并检错,而损坏的包可以被重传。
服务对象:
需要高度可靠性且面向连接的服务。
如HTTP(超文本协议)、FTP(文件传输协议)、SMTP(简单邮件传输协议)等。
(如果用的不都是TCP协议,就无法保证数据可以完整可靠的到达,从而出现问题,像文件传输、邮件传输、浏览网页,一定都是建立在TCP协议的基础上的。)
13.UDO协议简介
UDP协议——用户数据报协议。
UDP是一种面向无连接的传输层通信协议。
功能:
提供不同主机间上的进程间通信。
特点:
发送数据之前不需要建立链接。
不对数据包的顺序进行检查。(可能有顺序问题,但是快啊 )
没有错误检测和重传机制。
服务对象:
主要用于”查询-应答”的服务
如:NFS、NTP、DNS等
14.MAC地址、IP地址、Netmask、端口
1.网卡
网卡又称为网络适配器或网络接口卡NIC,但是现在更多的人愿意使用更为简单的名称’网卡’,通过网卡能够使不同的计算机之间连接,从而完成数据通信等功能。
2.MAC地址:
每一个网卡在出厂的时候都会给它分配一个编号,这个编号就叫做MAC地址。
MAC地址,用于标识网络设备,类似于身份证,且理论上全球唯一。
(MAC地址是可以修改的,但是一般是不建议修改的。)
MAC地址的组成:——以太网内的MAC地址是一个48bit的值,通常人为识别时是通过16进制数来识别的,以两个十六进制数为一组,一共分为6组,每组通过冒号隔开,前三组叫做,厂商ID,后三组叫做,设备ID.
如何查询Linux的MAC地址
1 | ifconfig |
如何查询 Windows的MAC地址
1 | 鼠标右键点击计算机右下角电脑图标,选择网络和共享中心,选择更改适配器设置,找到联网的图标,双击打开,点击详细信息,即可找到MAC地址。 |
3.IP地址
IP地址是一种Intenet上的主机编址方式,也称为网际协议地址。
IP地址是任意一台主机在网络中的唯一表示。
IP地址的分类:
IPV4——占32位,(主要用)
IPV6占128位,
(任意一个IP地址通过路由器都可以下分一个局域网。)
IP地址组成:
IPV4一般使用点分十进制字符串来表示,比如192.168.6.106
(ip地址有点类似于MAC地址,MAC地址是通过冒号把两个十六进制的数放一块来表示一组,一共有六组组成,而ipv4是通过点来把ip地址分开,相当于每一个点把它间隔1个字节,这是我们认为的时候是通过点分十进制的,但是如果实在网络中计算机识别,它还是一个整型的数据,占四个字节。)
使用32bit,由(网络、ID、主机 ID)两部分组成
子网ID:IP地址中由子网掩码中1覆盖的连续位。
主机ID:IP地址中由子网掩码中0覆盖的连续位。
子网ID和主机ID为什么这么区分呢?
——通过主机ID的范围可以确定当前一个网段到底可以下分多少个主机。

IP地址的特点:
子网ID不同的网络不能直接通信,如果要通信则需要路由器转发。
主机ID全为0的IP地址表示网段地址。
主机ID全为1(255个)的IP地址表示该网段的广播地址。
例如:192.168.3.10和192.168.3.111可以直接通信。
对192.168.3.x网段而言,192.168.3.0表示网段, 192.168.3.255表示广播地址 。
IP地址分类如下:
ABC三类地址是最常用的
IPV4地址的分类(依据前8位进行区分)
A类地址:默认8bit子网ID,第一位为0,前八位范围00000000-011111111,0.x.x.x-127.x.x.x
B类地址:默认16bit子网ID,前两位为10,前八位范围10000000-10111111,128.x.x.x-191.x.x.x
C类地址:默认24bit子网ID,前三位为110,前八位范围11000000-11011111 ,192.x.x.x-223.x.x.x
D类地址:前四位为1110,多播(组播)地址,前八位范围11100000-11101111, 224.x.x.x-239.x.x.x
E类地址:前五位为11110,保留为今后使用,前八位范围11110000-11111111,
240.x.x.x-255.x.x.x
公有IP:(可直接连接Internet)
经由InterNIC所统一规划的IP。
私有IP:(不可直接连接Internet)
主要用于局域网内的主机连接规划。

回环IP地址:
通常127.0.0.1称为回环地址。
功能:主要测试本机的网络配置,能ping通127.0.0.1说明本机的网卡和IP协议安装都没有问题。
注意:127.0.0.1-127.255.255.254中的任何地址都将回环到本地主机中,
不属于任何一个有类别地址类,它代表设备的本地虚拟接口。
查询IP地址的命令:
Linux——ifconfig
Windows——ipconfig
判断主机是否可以连接通信:
ping ip地址,
如果显示%0 packet loss,就表示可以正常通信。
子网掩码:
子网掩码(subnet mask)又叫做网络掩码、地址掩码是一个32bit由1和0组成的数值,并且1和0 分别连续。
作用:指明IP地址中哪些位标识的是主机所在的子网以及哪些是标识的主机号。
特点:必须结合IP地址一起使用,不能单独存在。
IP地址中由子网掩码中1覆盖的连续位为子网ID,其余为主机ID。
子网掩码的表现形式:
192.168.220.0/255.255.255.0
192.168.220.0/24
手动配置:
Linux——
默认的子网掩码:
A类IP地址的默认子网掩码-255.0.0.0
B类IP地址的默认子网掩码-255.255.0.0
C类IP地址的默认子网掩码-255.255.255.0(对应的网络ID是前三个字节,主机ID是最后一个字节,最多能给他分配256个IP地址,其中.0和.255分别表示网段和广播地址,所以一共可以分配254个IP地址)
4.端口:
端口概述
TPC/IP协议采用端口标识通信的进程
用于区分一个系统里的多个进程
特点:
对于同一个端口,在不同系统中对应着不同的进程。
对于同一个系统,一个端口只能被一个进程拥有。
一个进程拥有一个端口后,传输层送到该端口的数据全部被该进程接收,同样,进程送交给传送层的数据也通过该端口被送出。
端口号:
类似pid(进程号)标识一个进程,在网络程序中,用端口号(port)来标识一个运行的网络程序。
特点:
- 端口号是无符号短整型类型。
- 每个端口都有一个端口号。
- TPC、UDP维护各自独立的端口号。
- 网络应用程序,至少占用一个端口号,也可以占用多个端口号。
知名端口号(1~1023)
由互联网数字分配机构(IANA)根据用户需要进行统一分配
例如:FTP-21,HTTP-80
服务器通常使用的范围:
若强制使用,需加root权限 。
动态端口(1024~65535)
应用程序通常使用的范围。
一般我们可以使用的端口号就在这个范围,比如:6666、7777、8888、9999、10000、10001等。
注意:
端口号类似于进程号,同一时刻只能标识一个进程。
可以重复使用。
15.数据包的组装、拆解
1.数据包在各个层之间的传输

2.链路层封包模式

目的地址:目的MAC地址
源地址:源MAC地址
类型:确定以太网头后面跟的是哪个协议
0X0800 IP协议
0x0806 arp协议
0x0835 rarp协议
注意:
1、IEE802.2/802.3封装常用在无线。
2、以太网封装常用在有线局域网
3.网络层、传输层封包格式

16.网络应用程序开发流程
1.TPC——面向连接
电话系统服务模式的抽象。
每一次完整的数据传输都要经过建立连接、使用连接、终止连接的过程。
本质上,连接是一个管道,收发数据不但顺序一致,而且内容相同。
保证数据传输的可靠性。

2.UDP面向无连接
邮件系统服务模式的抽象。
每个分组都携带完整的目的地地址。
不能保证分组的先后顺序。
不进行分组出错的恢复和重传。
不保证数据传输的可靠性。

无论采用面向连接的还是无连接的,两个进程通信过程中,大多采用c/s架构(客户端 服务器架构),
client向server发送请求,server接收到后提供相应的服务。
在通信过程中往往都是client先发送请求,而server等待请求然后进行服务。

c/s架构示例,(面向连接)

服务器要先与客户端进行连接,TCP三次握手,三次握手成功之后,才可以相互发送数据,结束时,TCP四次挥手。
suerver工作过程
打开通信通道告诉本地主机,它愿意在一特定端口(如80)上接收客户请求,
等待客户请求到达该端口,
接收客户请求,并发送应答信号,激活一新的线程处理这个客户请求,
服务完成后,关闭新线程与客户的通信链路。
client工作过程
打开一通信通道并连接到服务器特定端口,
向服务器发送服务请求,等待并接收应答,
根据需求继续提出请求,
请求结束后关闭通信通道并终止。
2.UDP编程
1.编程准备-字节序、地址转换
1.字节序概述
是指多字节数据的存储顺序。
分类:
小端格式:将低位字节数据存储在低地址。
大端格式:将高位字节数据存储在低地址。
注意:
LSB:低地址。
MSB:高地址。

如何判断当期机器的字节序:
1 |
|
2.字节序转换函数
特点:
- 网络协议指定通信字节序为大端存储。
- 只有在字节数据处理时才需要考虑字节序。
- 运行在同一台计算机上的进程互相通信时,一般不用考虑字节序。
- 异构计算机之间通讯,需要转换自己的字节序为网络字节序。
在需要字节转换的时候一般调用特定字节序转换函数。
host —> network
1——htonl
2——htons
network —> host
3——ntohl
4——ntohs
1.htonl
头文件:
#include<arpa/inet.h>
uint32_t htonl(uint32_t hostint32);
功能:
将32位主机字节序数据转换成网络字节序数据
参数:
hostint32:待转换的32位主机字节序数据。
返回值:
成功:返回网络字节序的值.
2.htons
头文件:
#include<arpa/inet.h>
uint16_t htons(uint16_t hostint16);
功能:
将16位主机字节序数据转换成网络字节序数据
参数:
uint16_t: unsigned short int
hostint16:待转换的16位主机字节序数据。
返回值:
成功:返回网络字节序的值。
3.ntohl
头文件:
#include<arpa/inet.h>
uint32_t ntosl(uint32_t hostint32);
功能:
将32位网络字节序数据主机字节序数据。
参数:
uint32_t: unsigned int
netint32:待转换的32位网络字节序数据。
返回值:
成功:返回主机字节序的值。
4.ntohs
头文件:
#include<arpa/inet.h>
uint16_t ntosl(uint16_t hostint16);
功能:
将16位网络字节序数据主机字节序数据。
参数:
uint32_t: unsigned short int
netint32:待转换的16位网络字节序数据。
返回值:
成功:返回主机字节序的值。
案例:
1 | //Linux |
3.地址转换函数
人为识别的ip地址是点分十进制数串形式,但是计算机或者网络中识别的ip地址是整形数据,所以需要进行转化。
1.inet_pton函数
字符串 ip地址转整型数据
头文件:
#include<arpa/inet.h>
int inet_pton(int famliy,const char &strptr, void *addrptr)
人为识别的时候,ip地址就是一串字符串。让网络或者识别的时候,要将他转化为整型数据。用四字节的整数来保存整个ip地址。
功能:
将点分十进制的数转换成32位无符号整数。
参数;
family 协议族
AF_INET IPV4网络协议
AF_INET6 IPV6网络协议
strptr 点分十进制数串
addrptr 32位无符号整数的地址
返回值:
成功返回1、失败返回其他
案例:
1 |
|
结果:

2.inet_ntop函数
整型数据转字符串格式ip地址
头文件:
#include<arpa/inet.h>
const char *inet_ntop(int family,const void *addrptr,char *strptr,size_t len);
功能:
将32位无符号整数转换为点分十进制数串。
参数:
family 协议族
addrptr 32位无符号整数
strptr点分十进制数串
len strptr缓冲区长度
len 的宏定义
#define INET_ADDRSTRLEN 16 //for IPV4
#define INET_ADDRSTRLEN 46 //for IPV6
返回值:
成功:则返回字符串首地址。
案例;
1 |
|
结果:

3.inet_addr()和ine_ntoa()
只能用在IPV4的ip地址的转换中。
1 |
|
2.UDP介绍、编程流程
1.UDP概述
UDP协议:
面向无连接的用户数据协议,在传输数据前不需要先建立连接,目的主机的运输层收到UDP报文后,不需要给出任何确认。
UDP特点:
- 相比TCP速度稍快。
- 简单的请求/应答应用程序可以使用UDP.
- 对于海量的数据传输不应该使用UDP.
- 广播和多播应用必须使用 UDP。
UDP应用:
DNS(域名解析)、NFS(网络文件系统)、RTP(流媒体)等。
一般语音和视频通话都是使用UDP来通信的
2.网络的编程接口socket
网络通信要解决的是不用主机进程间的通信。
首要问题是网络间进程标识问题。
以及多重协议的识别问题。
20世纪80年代初,加州大学Berkeley分校在BSD(一个UNIX OS版本)系统内实现了TPC/IP协议;其网络程序编程开发接口为socket。
随着UNIX以及类UNIX的操作系统的广泛应用,socket成为最流行的网络程序开发接口。
(Linux操作系统就是类UNIX操作系统。)
socket作用:
提供不同主机上进程之间的通信。
socket特点:
- socket也成”套接字”。
- 是一种文件描述符,代表了一个通信管道的一个端点。
- 类似于对文件的操作一样(读写操作),可以使用read、write、close等函数对socket套接字进行网络数据的收取和发送等操作。
- 得到socket套接字(描述符)的方法调用socket()。
socket的分类:
SOCK_STREAM,流式套接字,用于TCP。
SOCK_DGRAM,数据报套接字,用于UDP。
SOCK_ROW,原始套接字,对于其他层次的协议操作时需要使用这个类型。
3.UDP编程c/s架构

(左边UDP客户端 右边UDP服务器 )
(服务器永远是被动的,客户端是主动的。)
UDP网络编程流程:
服务器:
创建套接字socket()
将服务器的IP地址、端口号与套接字进行绑定bind()
(为什么要进行绑定,只有当你进行绑定了之后,服务器所对应的信息才能固定,客户端才能找到服务器。)
接收数据recvfrom()
发送数据sendto()
客户端:
创建套接字socket()
发送数据sendto()
接收数据recvfrom()
关闭套接字close()
4.UDP编程-创建套接字
int socket(int family,int type,int protocol);
功能:
创建一个用于网络通信的socket套接字(描述符)。
参数:
family:协议族(AF_INET、AF_INET6、PF_PACKET等)
type:套接字类(SOCK_STREAM、SOCK_DGRAM、SOCK_RAW等)
protocol:协议族类(0、IPPROTO_TCP、IPPROTO_UDP等)
返回值:
套接字
特点:
创建套接字时,系统不会分配端口。
创建的套接字默认属性是主动的,即主动发起服务的请求;当作为服务器时,往往需要修改为被动的。
头文件:
#include<sys/socket.h>
5.创建UDP套接字demo
1 |
|
3.UDP编程 - 发送 、绑定、接收数据
1.IPV4套接字地址结构
**在网络编程中经常使用的结构体 **sockaddr_in
头文件:#include<netinet/in.h>
1 | struct in_addr |
为了使不同格式地址能被传入套接字函数,地址须要强制转换成通用套接字地址结构。
因为:不同场合使用的结构体不一样,但是调用的函数却是同一个,所以定义一个通用结构体,当在指定场合使用时,再根据要求传入指定的结构体即可。
通用结构体
sockaddr
#include<netinet/in.h>
1 | struct sockaddr |
注意:
以上三个结构在Linux系统中已经定义。
2.两种地址结构使用场合
在定义源地址和目的地址结构时,选用struct sockaddr_in
例:
struct sockaddr_in my_addr;
当调用编程接口函数,且该函数也需要传入地址结构时需要用struct sockaddr进行强制转换。
例:
bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr));
3.发送数据-sendto函数
ssize_t sendto(int sockfd const void *buf,
size_t nbytes,int flags,
const struct sockaddr *to,
socklen_t addrlen);
功能:
向to结构体指针中指定的ip,发送UDP数据。
参数:
sockfd:套接字
buf:发送数据缓冲区
nbytes:发送数据缓冲区的大小
flags:一般为0
to:指向目的主机地址结构体的指针
addrlen:to所指向内容的长度
注意:
to和addrlen确定目的地址
可以发送0长度的UDP数据包
返回值:
成功:发送数据的字符数
失败:-1
4.向”网络调试助手”发送消息
5.绑定 bind函数
UDP网络程序想要收取数据需要什么条件?
确定的ip地址
确定的port
怎样完成上面的条件呢?
接收端 使用bind函数,来完成地址结构和socket套接字的绑定,这样ip、port就固定了。
发送端 在sendto函数中指定接收端的ip、port,就可以发送数据了。
由于服务器是被动的,客户端是主动的,一般先运行服务器,后运行客户端,所以服务器需要固定自己的信息(ip、port),这样客户端才可以找到服务器并与之通信,客户端一般不需要bind绑定,因为系统会自动给客户端分配ip地址和端口号。
int bind(int sockfd,
const struct sockaddr * myaddr , socklen_t addrlen);
功能:
将本地协议地址与sockfd绑定。
参数:
sockfd: socket套接字
myaddr:指向特定协议的地址结构指针
addrlen:该地址结构的长度
返回值:
成功:返回0
失败:其他
6.bind示例
7.接收数据——recvfrom 函数
ssize_t recvfrom(int sockfd,void *buf,
size_t nbytes,int flags,
struct sockaddr *from,
socklen_t 8addrlen);
功能:
接收UDP数据 ,并将源地址信息保存在from指向的结构中。
参数:
sockfd:套接字
buf:接收数据缓区
flags:套接字标志(常为0)
from:源地址结构体指针,用来保存数据的来源
addrlen:from所指内容的长度
注意:
通过from和addrlen存放数据来源的信息
from和addrlen可以为NULL,表示不保存数据的来源
返回值:
成功:接收到的字符数
失败:-1
8.接收”网络调试助手”的数据
4.回顾
UDP编程-client、server
其实在网络编程开发中,clent和server双方既可以有发送数据还可以接收数据;一般认为服务的一方为server,而接受服务的一方为client.
C/S架构回顾
5.UDP客户端注意点
- 本地ip,本地端口(我是谁)
- 目的ip.目的端口(发给谁)
- 在客户端代码中,我们只设置了目的ip,目的端口。
6.UDP服务器注意点
- 服务器之所以需要bind是因为它的本地port是需要固定的,而不是随机的。
- 服务器也可以主动地给客户端发送数据。
- 客户端也可以用bind ,这样客户端的本地端口就是固定的了,但一般不这样做。







