❤️怒肝三万字,史诗的保姆网络编程教学❤️

Posted 魔动山霸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❤️怒肝三万字,史诗的保姆网络编程教学❤️相关的知识,希望对你有一定的参考价值。

网络编程概念

网络通信要解决的是不同主机进程间的通信
1、首要问题是网络间进程标识问题
2、以及多重协议的识别问题,其网络程 序编程开发接口为 socket 随着 UNIX 以及类 UNIX 操作系统的广泛应用, socket 成为最流行的网络程序开发接口
socket 作用
提供不同主机上的进程之间的通信
socket 特点
1、socket 也称“套接字”
2、是一种文件描述符,代表了一个通信管道的一个端点
3、类似对文件的操作一样,可以使用 read、write、close 等函数对 socket 套接字进行网络数据的收取和发 送等操作
4、得到 socket 套接字(描述符)的方法调用 socket()

1.字节序

概念:是指多字节数据的存储顺序
分类
1、大端(将高位字节数据存储在低地址)
2:小端(将低位字节数据存储在低地址)

2.字节序转换

主机字节序转网络字节序(网络字节序默认大端,而主机大部分为小端)
uint32_t htonl(uint32_t hostint32)//
uint16_t htons(uint16_t hostint16);

3.网络字节序—主机字节序

uint32_t ntohl(uint32_t netint32);
uint16_t ntohs(uint16_t netint16);

1、网络字节序一直是大端,且主机字节序转网络字节序就是转 大端保存,网络字节序转主机字节序就是大端转大端/小端。
2、只有在多字节数据处理时才需要考虑字节序。
3、同一主机间网络进程通信,不需要字节序转换。
4、不同主机间网络进程通信,需要字节序转换。

4.地址转换函数

点分十进制数转整数
int inet_pton(int family,const char *strptr, void *addrptr);
参数
1、协议族AF_INET
2、点分十进制串
3、转之后的整数
返回值:1成功
32位无符号整数转换成点分十进制数串
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
参数
1、协议族AF_INET
232位无符号整数
3、点分十进制串
4、点分十进制串缓冲区的长度(16)
返回值:成功:则返回字符串的首地址 失败:返回 NULL

UDP编程

1.概念

面向无连接的用户数据报协议,在传输数据前不需要先建立连接;目地主机的运输层收到 UDP 报文后,不需 要给出任何确认
特点:
1、相比 TCP 速度稍快些
2、简单的请求/应答应用程序可以使用 UDP
3、对于海量数据传输不应该使用 UDP
4、广播和多播应用必须使用 UDP
应用:DNS(域名解析)、NFS(网络文件系统)、RTP(流媒体)等



- UDP的通信过程

UDP编程核心代码介绍

发送数据—sendto 函数

 发送数据—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 数据包
返回值:

bind函数

UDP 网络程序想要收取数据需什么条件?
确定的 ip 地址
确定的 port
怎样完成上面的条件呢?
接收端 使用 bind 函数,来完成地址结构与 socket 套接字的绑定,这样 ip、port 就固定了
发送端 在 sendto 函数中指定接收端的 ip、port,就可以发送数据了
int bind(int sockfd,
const struct sockaddr *myaddr,socklen_t addrlen);
功能: 将本地协议地址与 sockfd 绑定
参数: 
sockfd: socket 套接字
myaddr: 指向特定协议的地址结构指针
addrlen:该地址结构的长度
返回值: 
成功:返回 0
失败:其他

recvform函数介绍

接收数据—recvfrom 函数
ssize_t recvfrom(int sockfd, void *buf,
size_t nbytes,int flags,
 struct sockaddr *from, 
 socklen_t *addrlen);
功能: 
接收 UDP 数据,并将源地址信息保存在 from 指向的结构中
参数: 
sockfd: 套接字
buf:接收数据缓冲区
nbytes:接收数据缓冲区的大小
flags: 套接字标志(常为 0)
from: 源地址结构体指针,用来保存数据的来源
addrlen: from 所指内容的长度
注意: 
通过 from 和 addrlen 参数存放数据来源信息
from 和 addrlen 可以为 NULL, 表示不保存数据来源
返回值: 
成功:接收到的字符数
失败: -1

2.UDP简单客户端代码演示

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc ,char **argv)
{
    //创建一个套接字,本质上是一个文件描述符
     struct sockaddr_in det;
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    printf("sockfd = %d\\n", sockfd);

    //指定发送给谁,绑定目的主机
    det.sin_family = AF_INET;
    det.sin_port = htons(8888);
    inet_pton(AF_INET, argv[1],&det.sin_addr);
    memset(det.sin_zero, 0, 8);
    printf("%u,%u\\n", det.sin_addr, det.sin_port);
    // int err = bind(sockfd, (struct sockadd *)&det, sizeof(det));
    // if(err < 0)
    // {
    //     printf("bind error\\n");
    // }
    while(1)
    {
        int fd = fork();
        if(fd == 0)
        {
            char buf[50];
            socklen_t form_S = sizeof(det);
            recvfrom(sockfd, buf, sizeof(buf),0,(struct sockaddr*)&det,&form_S);
            printf("from server:%s\\n",buf);
        }
        else
        {
            char *buf = (char *)malloc(20);
            fgets(buf, 20, stdin);
            *(buf + strlen(buf) - 1) = '\\0';
            int a = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&det, sizeof(det));
            printf("send num:%d\\n", a);

            if (strcmp(buf, "bye") == 0)
            {
                break;
            }
        }
        
    
    }
}

UDP简单服务器代码演示

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc ,char **argv)
{
    int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    int pipe_fd[2];
    pipe(pipe_fd);
    struct sockaddr_in my_addr;
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(10086);
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    int err = bind(sock_fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
    if(err == -1)
    {
        printf("bind error\\n");
    }

    while(1)
    {
        int fd = fork();
        if(fd ==0)
        {
            char buf[20];
            unsigned int client_ip;
            char from_ip[16]=" ";
            struct sockaddr_in client_addr;
            socklen_t  addrlen=sizeof(client_addr);
            client_addr.sin_family = AF_INET;
            client_addr.sin_port = htons(10086);
            read(pipe_fd[0],  (void *)&client_addr.sin_addr, sizeof( client_addr.sin_addr));
           // inet_pton(AF_INET, "10.36.145.41",&client_addr.sin_addr);
            printf("recevie :%s\\n",from_ip);
            while(1)
            {
                memset(buf, 0, sizeof(buf));
                fgets(buf, sizeof(buf), stdin);
                int err = sendto(sock_fd, buf, strlen(buf), 0,(struct sockaddr *)&client_addr, addrlen);
                printf("发送的数据%d\\n", err);
            }
        }
        else
        {
        char buf[20];
        char from_ip[16]=" ";
        struct sockaddr_in from_addr;
        bzero(&from_addr,sizeof(from_addr));
        memset(buf, 0, sizeof(buf));
        memset(from_ip, 0, sizeof(from_ip));
        socklen_t  addrlen=sizeof(from_addr);

        //接收到源ip
        recvfrom(sock_fd, buf, 20, 0, (struct sockaddr *)&from_addr,&addrlen);
        write(pipe_fd[1], (void *)&from_addr.sin_addr.s_addr, sizeof(from_addr.sin_addr.s_addr));
        //解析源ip的地址
        inet_ntop(AF_INET, (const void *)&from_addr.sin_addr.s_addr, from_ip,(socklen_t)16);
        printf("receive from :%s\\n", from_ip);
        printf("buf :%s\\n", buf);
       // sendto(sock_fd, buf, sizeof(buf), 0, (struct sockaddr *)&from_addr, addrlen);
        }
    }

}

UDP广播功能的实现

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>          /* See NOTES */

/*这是一个广播的程序,如何向局域网进行全体主机的广播,仅限于用在局域网
,城域网和广域网不适用,因为他们太多主机ip了    
*/
#if 0
int main(int argc,char**argv)
{
    struct sockaddr_in det;
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    printf("sockfd = %d\\n", sockfd);

    //更改mac地址为全f,修改套接字允许发送广播的消息
    int yes = 1;
     setsockopt(sockfd, SOL_SOCKET,SO_BROADCAST, &yes, sizeof(yes));

    //指定广播
    det.sin_family = AF_INET;
    det.sin_port = htons(8080);
    socklen_t addrlen = sizeof(det);
    inet_pton(AF_INET, argv[1],&det.sin_addr);
    memset(det.sin_zero, 0, 8);
    printf("%u,%u\\n", det.sin_addr.s_addr, det.sin_port);

    

    
    while(1)
    {
        char buf[64] = " ";
        fgets(buf, sizeof(buf), stdin);
        sendto(sockfd, buf,sizeof(buf), 0, (const struct sockaddr *)&det, addrlen);
    }
}
#endif

UDP多播功能的实现


/*
这是一个udp多播的最基本的演示的程序
*/
int main(int argc,char**argv)
{
    struct sockaddr_in det;
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    printf("sockfd = %d\\n", sockfd);

    char group[INET_ADDRSTRLEN] = "224.0.1.1";

    //定义一个多播地址
    struct ip_mreq mrep;

    //添加一个多播组IP
    mrep.imr_multiaddr.s_addr = inet_addr(group);

    //添加一个将要添加到多播组的ip
    mrep.imr_interface.s_addr = htons(INADDR_ANY);

    setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mrep, sizeof(mrep));

    //赋值
    det.sin_family = AF_INET;
    det.sin_port = htons(8080);

    /*转换过来就是0.0.0.0,泛指本机的意思
    ,也就是表示本机的所有IP,因为有些机子不止一块网卡,多网卡的情况下,
    这个就表示所有网卡ip地址的意思。
    比如一台电脑有3块网卡,分别连接三个网络,
    那么这台电脑就有3个ip地址了,如果某个应用程序需要监听某个端口,那他要监听哪个网卡地址的端口呢?*/
    det.sin_addr.s_addr = htons(INADDR_ANY);//添加自己本地的ip地址
    socklen_t addrlen = sizeof(det);   
    memset(det.sin_zero, 0, 8);

    //绑定
    bind(sockfd, (const struct sockaddr *)&det, addrlen);

    while(1)
    {
        char buf[64] = "";
        recvfrom(sockfd, buf, sizeof(buf), 0, NULL,NULL);
        printf("rece:%s\\n", buf);
    }
}
/*
多播实验总结
1.要先把当前ip加入到多播组的地址,然后客户端发送消息的时候会发送给 224.0.1.1,这个时候
会把消息发送给所有加入到多播组ip。如果不想了还可以把ip地址移出多播组
*/

TFTP协议介绍

1.1TFTP 概述

TFTP:简单文件传送协议
最初用于引导无盘系统,被设计用来传输小文件

特点:
基于 UDP 协议实现
不进行用户有效性认证

数据传输模式:
octet:二进制模式
netascii:文本模式
mail:已经不再支持

.1.2 TFTP 通信过程

  1. .服务器从固定端口69接收客户端得读写请求
  2. 接收到请求后服务器使用一个临时端口发送数据包给客户端,固定为512个字节,也可以改固定字节的长度
  3. 客户端接收到数据后发送一个ack说我已经收到了
  4. 如果服务器发送的数据包小于512个字节,那么代表服务器发送数据已经发完了,结束传输

1.3TFTP 协议分析

1.4客户端文件下载简单流程

  1. 创建udp套接字
  2. 组读请求的数据包(0,1,“文件名”,0,“模式”,0),发送给服务器
  3. 循环接收数据,数据包buf长度<516,退出,否则继续
  4. 查看数据包buf[1],如果buf[1] == 3 ;写文件,回复ack;buf[1] == 5;break;

1.5客户端上传文件简单流程

  1. 创建套接字
  2. 组写请求的数据包(0,1,“文件名”,0,“模式”,0)
  3. 客户端打开要上传的文件
  4. 循环发送数据buf,如果读本地文件长度<512,break;

1.6带选项的TFTP文件

tsize 选项
当读操作时,tsize 选项的参数必须为“0”,服务器会返回待读取的文件的大小
当写操作时,tsize 选项参数应为待写入文件的大小,服务器会回显该选项
blksize 选项
修改传输文件时使用的数据块的大小(范围:8~65464)
timeout 选项
修改默认的数据传输超时时间(单位:秒)

TFTP 通信过程总结(带选项)

1、可以通过发送带选项的读/写请求发送给 server
2、如果 server 允许修改选项则发送选项修改确认包
3、server 发送的数据、选项修改确认包都是临时 port
4、server 通过 timeout 来对丢失数据包的重新发送

TFTP 的下载过程的代码演示(不带选项)

我们来演示一下如何从服务器下载自己想要的文件存进里面
实现的是TFTP的客户端下载与上传功能,
TFTP软件下载链接
我们要和这个软件配套做实验

#include <stdio.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[]){
	//创建套接字
	int sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd<0){
		printf("创建失败\\n");
		return 0;
		}
	else{
		printf("创建成功 %d\\n",sockfd);
	}
	//给服务器发送下载请求
	struct sockaddr_in ser_addr;
	bzero(&ser_addr,sizeof(ser_addr));
	ser_addr.sin_family=AF_INET;
	ser_addr.sin_port=htons(69);
	inet_pton(AF_INET,"10.36.145.220",(void *)&ser_addr.sin_addr);
	char buf[64]以上是关于❤️怒肝三万字,史诗的保姆网络编程教学❤️的主要内容,如果未能解决你的问题,请参考以下文章

❤️万字Python基础保姆式教学❤️,小白快速入门Python!

❤️三万字《十大算法入门》❤️

❤️Python异常捕获和处理❤️保姆式教学,代码异常报错也能那么和谐且个性!

❤️Python异常捕获和处理❤️保姆式教学,代码异常报错也能那么和谐且个性!

❤️三万字《C语言面试错题100例》❤️(建议收藏)

❤️ 爆肝三万字《数据仓库体系》轻松拿下字节offer ❤️建议收藏