网络编程
- 计算机网络
- 概述
- 分类
- 网络体系结构
- 通信协议
- 通信流程
- 网络通信帧格式
- 以太网帧格式分析
- ARP 协议分析
- IP 数据报分析
- IP分类
- IP 分配
- 子网掩码
- TCP 段分析
- TCP三次握手协议 ⭐
- TCP四次挥手协议 ⭐
- TCP编程
- 基于 TCP 客户端编程-步骤说明
- 基于 TCP 服务器端编程-步骤说明
- 基于 TCP 服务端/客户端编程例子⭐
- 基于 TCP 服务端/客户端编程详细案例⭐
- 函数
- 创建套接字 socket
- 绑定函数 bind
- 大小端排序转换函数
- 地址格式转换函数
- 监听 listen
- 等待客户端连接 accept
- 连接服务器 connect
- 发送函数 send
- 接收函数 recv
- NAT映射
- 概述
- NAT 的优缺点
- 内网穿透(打洞)技术
- UDP编程
- 概念
- 通信流程
- 基于 UDP 客户端编程思路
- 基于 UDP 服务器端编程思路
- 基于UDP 客户端/服务端编程例子⭐
- 函数
- 发送函数 sendto
- 接收函数 recvfrom
- UDP广播
- 概念
- 广播特点
- 广播地址
- 网络编程服务器模型
- 回射服务器
- 迭代服务器
- 并发服务器
计算机网络
概述
计算机网络是指将不同地理位置,具有独立功能的多台计算机及网络设备通过通信线路(包括传输介质和网络设备连接起来),在网络操作系统、网络管理软件及网络通信协议的共同管理和协调下实现资源共享和信息传递的计算机系统。
分类
1 ) 地理范围:广域网 WAN、城域网 MAN、局域网 LAN、个人区域网 PAN。
2 ) 按拓扑结构:星型拓扑结构、总线型拓扑结构、环形拓扑结构、网状拓扑结构、混合拓扑结构。
3 ) 按网络使用者分类:公用网、专用网。
局域网(LoxalAreaNetwork,LAN)是指范围在几百米到十几公里内办公楼群或校园内的计算机相互连接所构成的计算机网络。计算机局域网被广泛应用于连接校园、工厂以及机关的个人计算机或工作站,以利于个人计算机或工作站之间共享资源和数据通信。
城域网(MetropolitanAreaNetwork,MAN)所采用的技术基本上与局域网相类似,只是规模上要大一些。城域网既可以覆盖相距不远的几栋办公楼,也可以覆盖一个城市;既可以是私人网,也可以是公用网。城域网既可以支持数据和话音传输,也可以与有线电视相连。城域网一般只包含一到两根电缆,没有交换设备,因而其设计就比较简单。
广域网(WidoAreaNetwork,WAN)通常跨接很大的物理范围,如一个国家。广域网包含很多用来运行用户应用程序的机器集合,我们通常把这些机器叫做主机;把这些主机连接在一起的是通信子网(communicationsubnet)。通信子网的任务是在主机之间传送报文。将计算机网络中的纯通信部分的子网与应用部分的主机分离开来,可以大大简化网络的设计。
网络体系结构
ISO 制定的 OSI 参考模型的过于庞大、复杂招致了许多批评。因此简化ISO的TCP/IP应运而生。
通信协议
TCP/IP:英文(Transmission Control Protocol/Internet Protocol)的缩写,传输控制协议/网际协议。
Telnet:是 TCP/IP 协议族中的一员,是 Internet 远程登陆服务的标准协议。
FTP:(文件传输协议)用于在网络上进行文件传输的一套标准协议,使用客户/服务器模式。
TCP:(传输控制协议) 为应用程序提供可靠的通信连接。适合于一次传输大批数据的情况。并适用于要求高的应用程序。
UDP:(用户数据包协议)提供了无连接通信,且不对传送包进行可靠的保证。适合于一次传输少量数据。
ICMP:(网络控制消息协议)用于发送报告有关数据包的传送错误的协议。
IGMP:(网络组管理协议)被 IP 主机用来向本地多路广播路由器报告主机组成员的协议。
IP:(网际互联协议)负责在主机和网络之间寻址和路由数据包。
ARP:(地址转换协议)用于获得同一物理网络中的硬件主机地址。
MPLS:(多协议标签交换)很有发展前景的下一代网络协议。
通信流程
网络通信帧格式
数据包在以太网物理介质上传播之前必须封装头部和尾部信息。封装后的数据包称为称为数据帧,数据帧中封装的信息决定了数据如何传输。以太网上传输的数据帧有两种格式,选择哪种格式由 TCP/IP 协议簇中的网络层决定。
网络通信中有两种数据帧格式:第一种是上世纪 80 年代初提出的 DIX v2 格式,即 Ethernet II 帧格式。Ethernet II 后来被 IEEE 802 标准接纳,并写进了 IEEE 802.3x-1997 的 3.2.6 节。以太网中大多数的数据帧使用的是 Ethernet II格式。本课程主要研究 Ethernet(以太网帧格式)在网络中传输过程。
网络通信帧格式
以太网帧格式分析
ARP 协议分析
IP 数据报分析
IP分类
按照 IP 版本分类:IPv4、IPv6。IPv4 的地址位数为 32 位,也就是最多有 2 的 32 次方(42 亿)的电脑可以连接: 近十年来由于互联网的蓬勃发展,IP 地址的需求量愈来愈大,导致 IPv4 定义的有限地址空间将被耗尽,地址空间的不足必将妨碍互联网的进一步发展。为了扩大地址空间,拟通过 IPv6 重新定义地址空间。IPv6 采用 128 位地址空间长度,几乎可以不受限制地提供地址。
按照状态分类:静态 IP 与动态 IP。动态 IP 需要在连接网络时自动获取 IP 地址以供用户正常上网,而静态 IP 是 ISP 在装机时分配给用户的 IP 地址,可以直接连接上网,不需要获取 IP 地址。
按照 IP 身份分类:公有 IP 与私有 IP。公有 IP:指以公网连接 Internet 上的非保留地址。私有 IP:是在本地局域网上的 IP。
每个 IP 地址都被分为两个部分即网络地址和主机地址。这样做的目的是为了在路由器转发数据包时更方便的寻址。网络位是用来确定网络的,就相当于你生活在哪个区域。主机位就是每一台电脑所用的 IP 地址,就相当于你所在区域有多少人,每个人的固定住所。
IP 分配
全为 0 表示任意 全为 1 表示广播
A 类地址范围:1.0.0.1—126.155.255.254
A 类:0xxxxxxx.hhhhhhhh.hhhhhhhh.hhhhhhhh 2^24-2=16777214;
B 类地址范围:128.0.0.1—191.255.255.254
B 类:10xxxxxx. xxxxxxxx.hhhhhhhh.hhhhhhhh 2^16-2=65534;
C 类地址范围:192.0.0.1—223.255.255.254
C 类:110xxxxx.xxxxxxxx.xxxxxxxx.hhhhhhhh 2^8-2=254;
D 类地址范围:224.0.0.1—239.255.255.254
E 类地址范围:240.0.0.1—255.255.255.254
在Linux系统中可以使用 ifconfig 指令先查看当前网络 IP 地址信息。
也可以使用 ifconfig ens33 指令修改 IP,例如:ifconfig ens33 192.168.1.64 netmask 255.255.255.0
最后可以使用 ping + ip 命令ping自己查看是否重置成功。也可以ping命令查看是否与目标主机连接。
子网掩码
子网掩码为网络掩码、地址掩码、子网络遮罩,一种用来指明一个 IP 地址的哪些位标识的是主机所在的子网,以及哪些位标识的是主机的位掩码。子网掩码不能单独存在,它必须结合 IP 地址一起使用。子网掩码只有一个作用,将某个 IP 地址划分成网络地址和主机地址两部分。子网掩码为一个 32 位地址,用于屏蔽 IP 地址的一部分以区别网络标识和主机标识,并说明该 IP 地址是在局域网上,还是在广域网上。
TCP 段分析
TCP三次握手协议 ⭐
TCP四次挥手协议 ⭐
TCP编程
套接字是操作系统内核中的一个数据结构,它是网络的节点进行相互通信的门户,它是网络进程的 ID。
端口号是具有网络功能的应用软件的标识号。端口是一个软件结构,被客户程序或服务程序用来发送和接收数据,一台服务器(或计算机)有 256*256 个端口。
基于 TCP 客户端编程-步骤说明
1.创建通信套接字:socket;
2.编写服务器地址信息(IP 端口 协议 TCP);
3.连接服务器:connect
4.发送信息/接收信息;send/rec | write/read
5.关闭通信套接字;close
基于 TCP 服务器端编程-步骤说明
1.创建监听套接字:socket
2.编写服务器地址信息;(IP 端口 协议 TCP);
3.将服务器地址信息与监听套接字绑定:bind;
4.开始监听:listen
5.等待客户端连接:accept(阻塞等待)
//三次握手
6.发送信息/接收信息 read/write
7.关闭通信套接字:close
基于 TCP 服务端/客户端编程例子⭐
客户端:具备基础通信功能
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>
typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;
#define MAXBACKLOG 100
int Socket(int domain,int type,int protocol);
int Connect(int sockfd,struct sockaddr * serv_addr,int addrlen);
void *son_fun(void * arg);
int main(int argc,char *argv[])
{
pthread_t id;
//建立监听套接字
int socketfd = Socket(AF_INET,SOCK_STREAM,0);
//connect
SIN serverinfo;
serverinfo.sin_family = AF_INET;
serverinfo.sin_port = htons(atoi(argv[2]));
serverinfo.sin_addr.s_addr = inet_addr(argv[1]);
int addrlen = sizeof(SIN);
Connect(socketfd,(SA*)&serverinfo,addrlen);
printf("服务器:%s 端口:%d\n",inet_ntoa(serverinfo.sin_addr),ntohs(serverinfo.sin_port));
//创建子线程
pthread_create(&id,NULL,son_fun,(void *)&socketfd);
//读写
while(1)
{
char readbuff[512]={0};
gets(readbuff);
if(strlen(readbuff)==0) continue;
write(socketfd,readbuff,sizeof(readbuff));
}
//关闭
close(socketfd);
return 0;
}
int Socket(int domain,int type,int protocol)
{
int socketFd = socket(domain,type,protocol);
if(socketFd == -1)
{
perror("socket");
exit(1);
}
return socketFd;
}
int Connect (int sockfd,struct sockaddr * serv_addr,int addrlen)
{
int val = connect(sockfd,serv_addr,addrlen);
if(val == -1)
{
perror("connect");
exit(1);
}
return 0;
}
void *son_fun(void * arg)
{
int readpipefd = *((int *)arg);
char readbuff[512]={0};
while(1)
{
if(read(readpipefd,readbuff,sizeof(readbuff))>0)
{
printf("接收:%s\n",readbuff);
}
else
{
close(readpipefd);
pthread_exit(NULL);
}
}
}
服务端:多线程服务端,具备基础通信功能
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>
typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;
#define MAXBACKLOG 100
int Socket(int domain,int type,int protocol);
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen);
int Listen(int s,int backlog);
int Accept(int s,struct sockaddr * addr,int * addrlen);
void *son_fun(void * arg);
int main(int argc,char *argv[])
{
//建立监听套接字
int socketfd = Socket(AF_INET,SOCK_STREAM,0);
//需要进行重用地址及其端口号
int opt = 1;
setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
//绑定信息编写服务器信息
SIN serverinfo;
serverinfo.sin_family = AF_INET;
serverinfo.sin_port = htons(atoi(argv[2]));
serverinfo.sin_addr.s_addr = inet_addr(argv[1]);
int addrlen = sizeof(SIN);
Bind(socketfd,(SA*)&serverinfo,addrlen);
//监听
Listen(socketfd,MAXBACKLOG);
//读写
while(1)
{
//等待连接
SIN clientinfo;
int clientaddrlen = sizeof(SA);
int newfd = Accept(socketfd,(SA*)&clientinfo,&clientaddrlen);
printf("客户端地址:%s 端口号:%d\n",inet_ntoa(clientinfo.sin_addr),ntohs(clientinfo.sin_port));
//创建子线程
pthread_t id;
pthread_create(&id,NULL,son_fun,(void *)&newfd);
}
//关闭
close(socketfd);
return 0;
}
int Socket(int domain,int type,int protocol)
{
int socketFd = socket(domain,type,protocol);
if(socketFd ==-1)
{
perror("socket");
exit(1);
}
return socketFd;
}
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen)
{
int val = bind(sockfd,my_addr,addrlen);
if(val)
{
perror("bind");
exit(1);
}
return 0;
}
int Listen(int s,int backlog)
{
int val = listen(s,backlog);
if(val == -1)
{
perror("listen");
exit(1);
}
return val;
}
int Accept(int s,struct sockaddr * addr,int * addrlen)
{
int NEWfd = accept(s,addr,addrlen);
if(NEWfd == -1)
{
perror("listen");
exit(1);
}
return NEWfd;
}
void *son_fun(void * arg)
{
int readpipefd = *((int *)arg);
char readbuff[512]={0};
while(1)
{
if(read(readpipefd,readbuff,sizeof(readbuff))>0)
{
printf("接收:%s\n",readbuff);
}
else
{
close(readpipefd);
pthread_exit(NULL);
}
}
}
基于 TCP 服务端/客户端编程详细案例⭐
点我查看
函数
创建套接字 socket
头文件:
#include<sys/types.h>
#include<sys/socket.h>
函数原型:: int socket(int domain,int type,int protocol);
参数介绍:
domain:指定使用何种的地址类型。
PF_INET/AF_INET Ipv4 网络协议
PF_INET6/AF_INET6 Ipv6 网络协议
type:
SOCK_STREAM 提供双向连续且可信赖的数据流,即 TCP。
SOCK_DGRAM 使用不连续不可信赖的数据包连接。
protocol:来指定 socket 所使用的传输协议编号,通常为0.
返回值:成功则返回 socket 文件描述符,失败返回-1。
int socketfd = socket(AF_INET,SOCK_STREAM,0);
绑定函数 bind
头文件:
#include<sys/types.h>
#include<sys/socket.h>
函数原型::int bind(int sockfd,struct sockaddr * my_addr,int addrlen);
参数介绍:
sockfd:socket 文件描述符。
my_addr:sockaddr 结构体。
addrlen:结构体长度。
返回值:功则返回0 ,失败返回-1,错误原因存于 errno 中。
//通用的套接字地址结构
struct sockaddr
{
unsigned short int sa_family;
char sa_data[14];
};
//IPv4 套接字地址结构
struct sockaddr_in
{
unsigned short int sin_family;
uint16_t sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr
{
uint32_t s_addr;
};
//初始化服务器信息
struct sockaddr_in serverinfo;
serverinfo.sin_family =AF_INET; //协议 IPV4
serverinfo.sin_port =htons(atoi(argv[2])); //网络字节序(大端字节序)与主机字节序(小端字节序)
serverinfo.sin_addr.s_addr= inet_addr(argv[1]);//192.168.5.166 2159634568 80.B9.68.88 128.185.104.136 点分十进制格式
//计算长度
int addrlen = sizeof(struct sockaddr_in);
//绑定
bind(socketfd,(struct sockaddr*)&serverinfo,addrlen);
大小端排序转换函数
主机字节序:主机内部内存中数据的处理方式。Intel 机器采用小端排序方式。
网络字节序:网络字节顺序是 TCP/IP 中规定好的一种数据表示格式,它与具体的 CPU 类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用大端排序方式。
这里提供一些端口格式转换函数:(n 代表网络,h代表本地,所以h to n 就是本地转换成网络)
地址格式转换函数
方便把 xxx.xxx.xxx.xxx 类型的地址与成十进制数字相互转换。
int inet_aton(const char *straddr,struct in_addr *addrptr);
作用:把点分十进制格式 IP 转换成 struct in_addr 类型 IP
char * inet_ntoa(struct in_addr inaddr);
作用:把 struct in_addr 类型的 IP 转换为点分十进制格式
in_addr_t inet_addr(const char *straddr);
作用:把点分十进制 IP 转换为 in_addr_t(uint32)类型 IP
示例:inet_aton(“192.168.1.1”,&ser_addr.sin_addr);
监听 listen
头文件:
#include<sys/socket.h>
函数原型:int listen(int s,int backlog);
参数介绍:
s:socket 文件描述符。
backlog:最大连接数。
返回值: 成功则返回 0,失败返回-1,错误原因存于 errno。
listen(socketfd,MAXBACKLOG);
等待客户端连接 accept
头文件:
#include<sys/types.h>
#include<sys/socket.h>
函数原型:int accept(int s,struct sockaddr * addr,int * addrlen);
参数介绍:
s:socket 文件描述符。
addr:远程主机的地址数据结构体。
addrlen:结构体大小。
返回值:成功则返回客户端的 socket 处理代码,失败返回-1,错误原因存于 errno 中。
struct sockaddr_in clientinfo;
int clientaddrlen =sizeof(SA);
int clientfd = accept(socketfd,(struct sockaddr*)&clientinfo,&clientaddrlen);
连接服务器 connect
头文件:
#include<sys/types.h>
#include<sys/socket.h>
函数原型:int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
参数介绍:
sockfd:socket 文件描述符。
addr:远程主机的地址数据结构体。
addrlen:结构体大小。
返回值:成功则返回 0,失败返回-1,错误原因存于 errno 中。
connect(socketfd,(struct sockaddr*)&serverinfo,addrlen);
发送函数 send
头文件:
#include<sys/types.h>
#include<sys/socket.h>
函数原型:int send(int s,const void * msg,int len,unsigned int falgs);
参数介绍:
s:socket 文件描述符。
msg:发送字符串。
len:字符串长度。
falgs:设置flags为0时,send和wirte是同等的。
返回值:
char sendbuff[128] = "11111";
send(socketfd,sendbuff,sizeof(sendbuff),0);
接收函数 recv
头文件:
#include<sys/types.h>
#include<sys/socket.h>
函数原型:int recv(int s,void *buf,int len,unsigned int flags);
参数介绍:
s:socket 文件描述符。
msg:接收字符串。
len:接收字符串长度。
falgs:设置flags为0时,recv和re’a’d是同等的。
返回值:
char readbuff[128] = {0};
send(socketfd,readbuff,sizeof(readbuff),0);
NAT映射
概述
网络地址转换(NAT)是一种用于访问 Internet 访问模式广域网(WAN)的技术,用于将私有(保留)地址转换为合法 IP 地址。NAT 不仅能够有效地额抵抗外部网络攻击(防火墙:外来连接),还能够在 IP 地址分配不理想,不足的时候有效,合理化的分配 IP 地址,从而能够进行互联网访问。
NAT 的优缺点
优点:
①极大的节省了合法的 IP 地址。
②能够处理地址重复情况,避免了地址的重新编号,增加了编址的灵活性。
③隐藏了内部网络地址,增强了安全性。
④可以使多个使用 TCP 负载特性的服务器之间实现基本的数据包负载均衡。
缺点:
①由于 NAT 要在边界路由器上进行地址的转换,增大了传输的延迟。
②由于 NAT 改动了 IP 地址,失去了跟踪端到端 IP 流量的能力。当出现恶意流量时,会使故障排除和流量跟踪变的更加棘手。
③不支持一些特定的应用程序。如早期版本的 MSN。
④增大了资源开销。处理 NAT 进程增加了 CPU 的负荷,并需要更多内存来存储 NAT 表项。
内网穿透(打洞)技术
NAT 机制导致:
服务器:私网 对 客户端:私网 需要打洞
服务器:公网 对 客户端:私网 无需打洞
服务器:私网 对 客户端:公网 需要打洞
服务器:公网 对 客户端:公网 无需打洞
了解更多
UDP编程
概念
UDP(user datagram protocol)的中文叫用户数据报协议,属于传输层(TCP/UDP)。UDP 是面向非连接的协议,它不与对方建立连接,而是直接把我要发的数据报发给对方。所以 UDP 适用于一次传输数据量很少、对可靠性要求不高的或对实时性要求高的应用场景。正因为 UDP 无需建立连接如三次握手,而使得通信效率很高。
通信流程
基于 UDP 客户端编程思路
1.建立 socket 套接字描述符
2.发送数据到服务器端
3.接收服务器端信息
4.关闭
基于 UDP 服务器端编程思路
1.服务器端开始建立 socket 描述符
2.编写服务器信息
3.sockfd 描述符与服务器进行绑定
4.接收客户端发送过来的数据
5.发送数据到客户端
6.关闭
基于UDP 客户端/服务端编程例子⭐
客户端:具备基础通信功能
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <time.h>
#include <pthread.h>
typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;
#define MAXBACKLOG 100
int Socket(int domain,int type,int protocol);
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen);
int Recvfrom(int s,void *buf,int len,unsigned int flags ,struct sockaddr *from ,int *fromlen);
int Sendto ( int s , const void * msg, int len, unsigned int flags, const struct sockaddr * to , int tolen );
int main(int argc,char *argv[])
{
//建立监听套接字
int socketfd = Socket(AF_INET,SOCK_DGRAM,0);
//绑定信息编写服务器信息
SIN serverinfo;
serverinfo.sin_family =AF_INET;
serverinfo.sin_port =htons(atoi(argv[2]));
serverinfo.sin_addr.s_addr= inet_addr(argv[1]);
int addrlen = sizeof(SIN);
//读写
while(1)
{
char readbuff[512] = {0};
int fromlen = sizeof(SIN);
gets(readbuff);
//回发
Sendto (socketfd,readbuff,strlen(readbuff), 0,(SA*)&serverinfo,fromlen);
}
//关闭
close(socketfd);
return 0;
}
int Socket(int domain,int type,int protocol)
{
int socketFd = socket(domain,type,protocol);
if(socketFd ==-1)
{
perror("socket");
exit(1);
}
return socketFd;
}
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen)
{
int val = bind(sockfd,my_addr,addrlen);
if(val)
{
perror("bind");
exit(1);
}
return 0;
}
int Recvfrom(int s,void *buf,int len,unsigned int flags ,struct sockaddr *from ,int *fromlen)
{
int val = recvfrom(s,buf,len,flags ,from ,fromlen);
if(val == -1)
{
perror("recvfrom");
exit(1);
}
return 0;
}
int Sendto ( int s , const void * msg, int len, unsigned int flags, const struct sockaddr * to , int tolen )
{
int val = sendto (s ,msg,len,flags,to ,tolen );
if(val == -1)
{
perror("sendto");
exit(1);
}
return 0;
}
服务端:具备基础通信功能
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <time.h>
#include <pthread.h>
typedef struct sockaddr SA;
typedef struct sockaddr_in SIN;
#define MAXBACKLOG 100
int Socket(int domain,int type,int protocol);
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen);
int Recvfrom(int s,void *buf,int len,unsigned int flags ,struct sockaddr *from ,int *fromlen);
int Sendto ( int s , const void * msg, int len, unsigned int flags, const struct sockaddr * to , int tolen );
int main(int argc,char *argv[])
{
//建立监听套接字
int socketfd = Socket(AF_INET,SOCK_DGRAM,0);
//绑定信息编写服务器信息
SIN serverinfo;
serverinfo.sin_family =AF_INET;
serverinfo.sin_port =htons(atoi(argv[2]));
serverinfo.sin_addr.s_addr= inet_addr(argv[1]);
int addrlen = sizeof(SIN);
Bind(socketfd,(SA*)&serverinfo,addrlen);
//读写
while(1)
{
char readbuff[512]={0};
SIN from;
int fromlen = sizeof(SIN);
Recvfrom(socketfd,readbuff,sizeof(readbuff),0 ,(SA*)&from ,&fromlen);
printf("ip:%s port:%d Data:%s\n",inet_ntoa(from.sin_addr),ntohs(from.sin_port),readbuff);
//回发
Sendto (socketfd,readbuff,strlen(readbuff), 0,(SA*)&from,fromlen);
}
//关闭
close(socketfd);
return 0;
}
int Socket(int domain,int type,int protocol)
{
int socketFd = socket(domain,type,protocol);
if(socketFd ==-1)
{
perror("socket");
exit(1);
}
return socketFd;
}
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen)
{
int val = bind(sockfd,my_addr,addrlen);
if(val)
{
perror("bind");
exit(1);
}
return 0;
}
int Recvfrom(int s,void *buf,int len,unsigned int flags ,struct sockaddr *from ,int *fromlen)
{
int val = recvfrom(s,buf,len,flags ,from ,fromlen);
if(val == -1)
{
perror("recvfrom");
exit(1);
}
return 0;
}
int Sendto ( int s , const void * msg, int len, unsigned int flags, const struct sockaddr * to , int tolen )
{
int val = sendto (s ,msg,len,flags,to ,tolen );
if(val == -1)
{
perror("sendto");
exit(1);
}
return 0;
}
函数
发送函数 sendto
头文件:
#include<sys/socket.h>
函数原型:int sendto(int s,const void *msg,int len,unsigned int flags,const struct sockaddr * to,int tolen);
参数介绍:
s:套接字文件描述符。
msg:指向要发送数据的容器地址。
len:要发送的数据长度。
flags:一般为 0 阻塞等待。
to:目地机的 ip 地址和端口号信息。
tolen:地址长度。
返回值:成功返回发送的字节数,出错返回-1。
struct sockaddr_in to;
int tolen = sizeof(struct sockaddr_in);
char sendbuff[512] = {0};
Sendto(socketfd,sendbuff,strlen(sendbuff), 0,(struct sockaddr*)&to,tolen);
接收函数 recvfrom
头文件:
#include<sys/socket.h>
函数原型:int recvfrom(int s,void *buf,int len,unsigned int flags ,struct sockaddr *from ,int *fromlen);
参数介绍:
s:套接字文件描述符。
buf:指向要存储数据容器的地址。
len:要保存的数据长度。
flags:—般为 0 阻塞等待。
from:源主机的 ip 地址和端口号信息。
formlen:地址长度。
返回值:成功返回接收的字节数,出错返回-1。
struct sockaddr_in from;
int fromlen = sizeof(struct sockaddr_in);
char readbuff[512] = {0};
Recvfrom(socketfd,readbuff,sizeof(readbuff),0,(struct sockaddr*)&from ,&fromlen);
UDP广播
概念
广播 UDP 与单播 UDP 的区别就是 IP 地址不同,广播使用广播地址 255.255.255.255,将消息发送到在同一广播网络上的每个主机。值得强调的是:本地广播信息是不会被路由器转发。当然这是十分容易理解的,因为如果路由器转发了广播信息,那么势必会引起网络瘫痪。这也是为什么 IP 协议的设计者故意没有定义互联网范围的广播机制。广播地址通常用于在网络游戏中处于同一本地网络的玩家之间交流状态信息等。其实广播顾名思义,就是想局域网内所有的人说话,但是广播还是要指明接收者的端口号的,因为不可能接受者的所有端口都来收听广播。
广播特点
1.数据传输不用建立连接,所以不可靠(符合 udp 协议的特点)。
2.数据的发送是面向整个子网的,任何一台在子网内的计算机都可以接收到相同的数据。
3.广播用于 udp 和原始 IP,不能用于 TCP。
广播地址
1.直接广播地址:
指 Host 部分全为 1 的广播地址。如:192.168.0.255。当某机器发出目的地址为直接广播(如:192.168.199.255)时,路由器通过查找路由表可以转发,直到该网段。
2.受限广播地址:
也称本地广播地址,它不被路由发送,但会被送到相同物理网络段上的所有主机,IP 地址的网络号和主机号全为 1 就是地址 255.255.255.255,当某机器发出目的地址为本地广播时,路由器不会转发该包。所以该包只能限制在本网段。
网络编程服务器模型
回射服务器
即接收客户端的消息,把消息原封不动的返回去。
迭代服务器
在多个客户端连接的时候,服务器在同一时刻只能响应一个客户端的请求。
并发服务器
在多个客户端连接的时候,服务器在同一时刻可以响应多个客户端的请求。