Linux C 网络编程概述

news2024/11/15 11:58:28

网络编程

  • 计算机网络
    • 概述
    • 分类
    • 网络体系结构
    • 通信协议
    • 通信流程
      • 网络通信帧格式
        • 以太网帧格式分析
        • 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,当某机器发出目的地址为本地广播时,路由器不会转发该包。所以该包只能限制在本网段。

网络编程服务器模型

回射服务器

  即接收客户端的消息,把消息原封不动的返回去。

迭代服务器

  在多个客户端连接的时候,服务器在同一时刻只能响应一个客户端的请求。

并发服务器

  在多个客户端连接的时候,服务器在同一时刻可以响应多个客户端的请求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1234779.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TypeError: expected np.ndarray (got Tensor)解决办法

文章目录 一、错误展示二、错误分析三、解决办法四、其余解决办法总结 一、错误展示 二、错误分析 这个错误表示正在尝试将一个PyTorch的Tensor对象作为numpy的ndarray对象来使用。我们需要使用numpy的ndarray而不是PyTorch的Tensor。 三、解决办法 在我的程序中去掉这一行代…

Redis跳跃表

前言 跳跃表(skiplist)是一种有序数据结构&#xff0c;它通过在每一个节点中维持多个指向其他节点的指针&#xff0c;从而达到快速访问节点的目的。 跳跃表支持平均O(logN)&#xff0c;最坏O(N)&#xff0c;复杂度的节点查找&#xff0c;还可以通过顺序性来批量处理节点…

局域网文件共享神器:Landrop

文章目录 前言解决方案Landrop软件界面手机打开效果 软件操作 前言 平常为了方便传文件&#xff0c;我们都是使用微信或者QQ等聊天软件&#xff0c;互传文件。这样传输有两个问题&#xff1a; 必须登录微信或者QQ聊天软件。手机传电脑还有网页版微信&#xff0c;电脑传手机比…

鸿蒙4.0开发笔记之DevEco Studio启动时不直接打开原项目(二)

1、想要在DevEco Studio启动时不直接打开关闭前的那个项目&#xff0c;可以在设置中进行。 有两个位置可以进入“设置”&#xff0c;一个是左上角的File>Settings&#xff0c;二是右上方的设置图标。 2、进入Settings界面以后&#xff0c;选择Appearance&Behavior下面…

【C++】泛型编程 ⑨ ( 类模板的运算符重载 - 函数声明 和 函数实现 写在同一个类中 | 类模板 的 外部友元函数问题 )

文章目录 一、类模板 - 函数声明与函数实现分离1、函数声明与函数实现分离2、代码示例 - 函数声明与函数实现分离3、函数声明与函数实现分离 友元函数引入 二、普通类的运算符重载 - 函数声明 和 函数实现 写在同一个类中三、类模板的运算符重载 - 函数声明 和 函数实现 写在同…

AMEYA360:蔡司扫描电镜Sigma系列:扫描电子显微镜的用途原来这么多

扫描电子显微镜是一种全自动的、非破坏性的显微分析系统&#xff0c;可针对无机材料和部分有机材料&#xff0c;迅速提供在统计学上可靠且可重复的矿物学、岩相学和冶金学数据&#xff0c;在采矿业&#xff0c;可用于矿产勘查、矿石表征和选矿工艺优化&#xff0c;在石油和天然…

Oracle数据库笔记(一)

1.概述 Oracle版本 19c 在线迁移、自适应扫描、自适应数据共享11g 企业管理器、自动化诊断工具、自动化性能管理 Oracle特点 可用性强可扩展性强数据安全性强稳定性强 常见数据库 小 Access中 SQL Server、MySQL大 Oracle、DB2 2.数据、数据库、数据库管理系统、数据库系…

【项目管理】甘特图(1)——认识甘特图

哈喽&#xff0c;大家好&#xff0c;我是雷工&#xff01; 今天学习下甘特图的绘制&#xff0c;以下为学习笔记。 一、什么是甘特图 甘特图是可以直观展示项目进展随时间走势和联系的条状图。是一种常见的项目管理工具。 项目的时间用横轴表示&#xff0c;项目的进度用纵轴表…

vue diff算法原理以及v2v3的区别

diff算法简介 diff算法的目的是为了找到哪些节点发生了变化&#xff0c;哪些节点没有发生变化可以复用。如果用最传统的diff算法&#xff0c;如下图所示&#xff0c;每个节点都要遍历另一棵树上的所有节点做比较&#xff0c;这就是o(n^2)的复杂度&#xff0c;加上更新节点时的…

可视化大屏时代的到来:智慧城市管理的新思路

随着科技的不断发展&#xff0c;智能芯片作为一种新型的电子元件&#xff0c;被广泛应用于各个领域&#xff0c;其中智慧芯片可视化大屏是一种重要的应用形式。 一、智慧芯片可视化大屏的优势 智慧芯片可视化大屏是一种将智能芯片与大屏幕显示技术相结合的产品&#xff0c;山海…

算法分析与设计课后练习23

求下面的0-1背包问题 &#xff08;1&#xff09;N5,M12,(p1,p2,…,p5)(10,15,6,8,4),(w1,w2,…,w5)(4,6,3,4,2) &#xff08;2&#xff09;N5,M15,(p1,p2,…,p5)(w1,w2,…,w5)(4,4,5,8,9)

Springboot+vue的社区医院管理系统(有报告),Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的社区医院管理系统(有报告)&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的应急物资管理系统&#xff0c;采用M&#xff08;model&#xff09;V&am…

【Django使用】4大模块50页md文档,第4篇:Django请求与响应和cookie与session

当你考虑开发现代化、高效且可扩展的网站和Web应用时&#xff0c;Django是一个强大的选择。Django是一个流行的开源Python Web框架&#xff0c;它提供了一个坚实的基础&#xff0c;帮助开发者快速构建功能丰富且高度定制的Web应用 Django全套笔记地址&#xff1a; 请移步这里 …

动态规划十大经典问题

动态规划十大经典问题 动态规划十大经典问题 数塔取数问题、矩阵取数问题、最大连续子段和、最长递增子序列、最长公共子序列、最长公共子串、最短编辑距离、背包问题、正整数分组、股票买卖问题。 1、数塔取数问题 // 数塔取数问题 public static int dataTowerAccess(int[]…

一文读懂 Linux 网络 IO 模型

文章目录 1.从一个问题说起2.多进程模型3.多线程模型4.I/O 多路复用5.select、poll、epoll 的区别&#xff1f;5.1 select5.2 poll5.3 epoll5.4 两种事件触发模式 参考文献 1.从一个问题说起 互联网发展历史上&#xff0c;曾经有一个著名的问题&#xff1a;C10K 问题。 C 是 …

5-4计算一串字符的空格数字字符其他

#include<stdio.h> int main(){char c;int space0;//空格int letters0;//英文字母int numbers0;//数字int others0;//其他字符printf("请输入一行字符&#xff1a;");while((cgetchar())!\n)//获取字符的内容&#xff0c;到\n停止{if(c>a&&c<z|…

AR眼镜方案—单目光波导AR智能眼镜

光波导技术是一项具有前沿意义的技术&#xff0c;它能够将光线反射180度&#xff0c;使得眼镜框架内置的MicroLED屏幕的图像通过多次反射与扩散后准确地传递到人眼中。采用MicroLED显示技术的AR智能眼镜不仅体积显著缩小&#xff0c;屏幕只有0.68英寸大小&#xff0c;并且还能够…

【PostgreSQL】解决PostgreSQL时区(TimeZone)问题

问题描述 最近在使用PostgreSQL中&#xff0c;对行记录进行设置创建时间&#xff08;created_time&#xff09;时&#xff0c;出现了设置了now()时间而数据库中写入的数据是不一致的数据。 eg&#xff1a; insert into dept ( created_at, updated_at) VALUES (now(),now())…

Spring框架学习 -- 核心思想

目录 (1) Spring是什么? (2) 什么是IOC容器? (3) 从传统开发认识spring (4) 这种传统开发的缺陷 (5)解决传统开发中的缺陷 (6) 对比总结规律 (7) 理解IOC 创作不易多多支持 (1) Spring是什么? 我们常说的Spring的全称是: Spring Framework(Spring框架), 它是一个开源…

【经验之谈·高频PCB电路设计常见的66个问题】

文章目录 1、如何选择PCB 板材&#xff1f;2、如何避免高频干扰&#xff1f;3、在高速设计中&#xff0c;如何解决信号的完整性问题&#xff1f;4、差分布线方式是如何实现的&#xff1f;5、对于只有一个输出端的时钟信号线&#xff0c;如何实现差分布线&#xff1f;6、接收端差…