net、udp、tcp

Makefile的main.c文件中的全局变量SONG song,要在fun.c文件里面写成extern SONG song

编译方法

第一次编写


网络编程 

物理层的网线规定有八根,颜色不一样,功能不一样,光猫把光信号转换成电信号,光纤10Gb

WiFi叫无线局域网,一般也就50米左右,手机流量叫蜂窝网络,随时随地上网。

链路层:先格式化一下,网络可以通过交换机连接起来。

网络层:通过ip地址找到目标

传输层:数据以何种方式传输?tcp可靠传输,传输过程中数据准确的到达对方。udp可能会丢包 。一般下载文件上传文件多以tcp为主,一般视频,音频 (实时性重要)一般用udp

OSI模型

物理层

解决两个硬件之间怎么通信的问题,常见的物理媒介有光纤、电缆、中继器等。它主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。

它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。

链路层(把设备连接起来)

在计算机网络中由于各种干扰的存在,物理链路是不可靠的。该层的主要功能就是:通过各种控制协议,将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链路。

它的具体工作是接收来自物理层的位流形式的数据,并封装成帧,传送到上一层;同样,也将来自上层的数据帧,拆装为位流形式的数据转发到物理层。这一层的数据叫做帧。

网络层

计算机网络中如果有多台计算机,怎么找到要发的那台?如果中间有多个节点,怎么选择路径?这就是路由要做的事。

该层的主要任务就是:通过路由选择算法,为报文(该层的数据单位,由上一层数据打包而来)通过通信子网选择最适当的路径。这一层定义的是IP地址,通过IP地址寻址,所以产生了IP协议。

传输层

当发送大量数据时,很可能会出现丢包的情况,另一台电脑要告诉是否完整接收到全部的包。如果缺了,就告诉丢了哪些包,然后再发一次,直至全部接收为止。

简单来说,传输层的主要功能就是:监控数据传输服务的质量,保证报文的正确传输。

会话层(网络断开,连接的状态)

虽然已经可以实现给正确的计算机,发送正确的封装过后的信息了。但我们总不可能每次都要调用传输层协议去打包,然后再调用IP协议去找路由,所以我们要建立一个自动收发包,自动寻址的功能。于是会话层出现了:它的作用就是建立和管理应用程序之间的通信。

表示层(加密,解密)

表示层负责数据格式的转换,将应用处理的信息转换为适合网络传输的格式,或者将来自下一层的数据转换为上层能处理的格式。

应用层

应用层是计算机用户,以及各种应用程序和网络之间的接口,其功能是直接向用户提供服务,完成用户希望在网络上完成的各种工作。前端同学对应用层肯定是最熟悉的。

TCP/IP模型(也叫tcp/ip协议栈):专门描述互联网的模型(把OSI模型合并了一下)

TCP/IP协议族

  • dns 域名解析  把域名转成ip地址
  • DHCP 动态主机配置协议(路由器分配地址)
  • http  超文本传输协议(使用网页就会用到这个协议)
  • FTP  互联网远距离传输用ftp(可以断点续传)
  • TFTP 简单文件传输协议(传输本地文件)---局域网内
  • SNMP   网络管理  网络状态监测
  • TCP 传输控制协议   一种可靠的传输方式  自带超时重传   必须要有应答    实时性比较差
  • UDP   实时性好  会丢包   节省网络开销
  • ARP  地址解析协议    在以太网环境中,数据的传输所依懒的是MAC地址而非IP地址,而将已知IP地址转换为MAC地址的工作是由ARP协议来完成的。
  • RIP和OSPF都是路由协议

TCP编程基础知识


  • 网关:也叫gate,跟个门一样,没出去就是局域网,出去就是互联网,默认是0.1(后两位)
  • 广播:最后一位是255)给这个位发,所有人都接收到了(只能是局域网里,一个人发所有人收)
  • 组播:让想收到的人收到(类似于群聊)

IP地址一般都是点分十进制(分为四段,每一段最小0最大255)

A类中第一段表示有多少个网络,是网络号,后面的数字表示有多少台主机(2的64次方)

B类中前两段表示有多少个网络(比如说128.0和128.1代表两个网络),后面数字表示有多少台主机(2的16次方)

C类前三段表示有多少个网络,后面数字表示有某一台主机(主机号)------我们一般用的是这种

linux的网络配置命令

配置完一定要重启sudo reboot 才会生效

ifconfig:查看网卡状态相关信息

硬件地址

手动设置ip地址,然后重启

查看网络状态(是否连接)

网络接口

  • socket 套接字  是文件描述符(网络文件需要收发)
  • ip地址是用来找主机的
  • 端口号用来识别进程的

网络字节序

小端存储--数据的低位在低内存

  • 计算机是小端    网络设备是大端
  • ip地址和端口号要去找主机设备--------都要大小端转换一下

UDP(有服务端和客户端,一对多的关系)简单一点----无连接,不显示状态信息,不可靠

  • socket类似于open,将网络设备打开,打开后拿到一个文件描述符
  • 客户端找服务器需要ip地址和端口号,bind就是给套接字设置ip地址和端口号的 
  • 服务器先收,因为客户得先告诉服务器要啥

代码:(客户端找服务器)

固定的就选这个IPv4对应的

服务端: 

SA是强转一下,转成下面这个类型

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);
int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);//第一步,可能会有少量丢包,0是默认方式
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }

    // man 7 ip 
    struct sockaddr_in ser,cli;//in代表internet互联网,这个是对方和自己的地址结构体
    bzero(&ser,sizeof(ser));
    bzero(&cli,sizeof(cli));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);//小端转大端,50000是端口号
    ser.sin_addr.s_addr = inet_addr("192.168.203.128");//写自己ip,大端转小端,数字转字符串
    int ret = bind(sockfd,(SA)&ser,sizeof(ser));//第二步,绑定
    if(-1 == ret)
    {
        perror("bind");
        exit(1);
    }
    socklen_t len = sizeof(cli);
    while(1)
    {
        char buf[512]={0};
        recvfrom(sockfd,buf,sizeof(buf),0,(SA)&cli,&len);//第三步,收,0代表工作方式,有人发就接受,没人就等着
        time_t tm;
        time(&tm);//拿到时间
        sprintf(buf,"%s %s",buf,ctime(&tm));
        sendto(sockfd,buf,strlen(buf),0,(SA)&cli,len);//第四步,发出去,0是默认方式
    }
    close(sockfd);
    return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);


int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);//第一步
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }
    struct sockaddr_in ser;
    bzero(&ser,sizeof(ser));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);
    ser.sin_addr.s_addr = inet_addr("192.168.203.128");

    while(1)
    {
        char buf[512]="hello,this is udp test";
        sendto(sockfd,buf,strlen(buf),0,(SA)&ser,sizeof(ser));//第二步
        bzero(buf,sizeof(buf));
        recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);//第三步
        printf("buf is %s\n",buf);
        sleep(1);
    }
    close(sockfd);
    return 0;
}

 结果:

UDP(用户数据报)(半双工,要么收要么发)

 发完链路就自动释放了,比较节省空间,网络开销比较小,丢包就是因为没有维护链路的状态

数据报:数据和数据间是有间隔的,收发次数要对应,不然就丢了,并且在收的大小建议大于等于包的大小,即使小了,剩下的也不会再收到了

UDP特征

  • 数据有边界
  • 收发次数要对应
  • recvfrom如果没有发送,就会阻塞
  • sendto不会阻塞,他只管发,收不收都可以

UDP传文件的代码

服务端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
#include <fcntl.h>
typedef struct sockaddr * (SA);
int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }

    // man 7 ip 
    struct sockaddr_in ser,cli;
    bzero(&ser,sizeof(ser));
    bzero(&cli,sizeof(cli));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);
    //ser.sin_addr.s_addr = inet_addr("127.0.0.1");//活的地址,也代表自己的地址,本地回环,自己跟自己测,这个数据不会发出去
    ser.sin_addr.s_addr = INADDR_ANY;//代表本机任何一个可用的IP地址,自己跟自己也可以,跟外面也可以,不用大小端转换
    int ret = bind(sockfd,(SA)&ser,sizeof(ser));
    if(-1 == ret)
    {
        perror("bind");
        exit(1);
    }
    socklen_t len = sizeof(cli);
    int fd = open("2.png",O_WRONLY|O_CREAT|O_TRUNC,0666);//trunc是清空,只要有创建后面就给666
    if(-1 == fd)
    {
        perror("open");
        exit(1);
    }
    while(1)
    {
        char buf[512]={0};
        int rd_ret = recvfrom(sockfd,buf,sizeof(buf),0,(SA)&cli,&len);
        if(0 == strcmp(buf,"^_^"))
        {
            break;
        }
        write(fd,buf,rd_ret);//收多少写多少
        bzero(buf,sizeof(buf));
        strcpy(buf,"go on");//防止发的太快来不及写(收),所以控制他发的速度
        sendto(sockfd,buf,strlen(buf),0,(SA)&cli,len);
    }
    close(sockfd);
    close(fd);
    return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
#include <fcntl.h>
typedef struct sockaddr * (SA);


int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }
    struct sockaddr_in ser;
    bzero(&ser,sizeof(ser));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);
    ser.sin_addr.s_addr = inet_addr("192.168.203.128");//自己跟自己可以写any,跟别的必须写客户端IP地址
    int fd = open("/home/linux/1.png",O_RDONLY);
    if(-1 == fd)
    {
        perror("open");
        exit(1);
    }
    char buf[512]={0};
    while(1)
    {
        bzero(buf,sizeof(buf));
        int rd_ret = read(fd,buf,sizeof(buf));
        if(0==rd_ret)
        {
            break;
        }
        sendto(sockfd,buf,rd_ret,0,(SA)&ser,sizeof(ser));
        bzero(buf,sizeof(buf));
        recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
    }
    
    bzero(buf,sizeof(buf));
    strcpy(buf,"^_^");
    sendto(sockfd,buf,3,0,(SA)&ser,sizeof(ser));//走到这里证明发送结束了,告诉客户端发送结束了,因为无连接
    close(sockfd);
    close(fd);
    return 0;
}

结果:

练习:UDP的聊天室(一个人发,全都能收到)

服务端是负责转发的,除了不给发消息的人转发,其他人都转

服务端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);
typedef enum {CMD_LOGIN,CMD_CHAT,CMD_LOGOUT}TYPE;
typedef struct 
{
    TYPE type;
    char name[50];
    char context[128];

}MSG;
typedef struct 
{
    struct sockaddr_in cli;
    int flag; // 0  free 1 occu
}LIST;
#define MAX 10
LIST list[MAX]={0};//最多多少人
int do_login(int sockfd,MSG* msg,struct sockaddr_in* cli)
{
    int i = 0 ;
    for(i=0;i<MAX;i++)
    {
        if(1 == list[i].flag )//等于一证明人在,没退出
        {
            sendto(sockfd,msg,sizeof(MSG),0,(SA)&list[i].cli,sizeof(list[i].cli));
        }
    }
    for(i=0;i<MAX;i++)
    {
        if(0 == list[i].flag )
        {
            list[i].flag =1;
            //list[i].cli = *cli;
            memcpy(&list[i].cli,cli,sizeof(*cli));
            break;
        }
    }
    return 0;
}

int do_chat(int sockfd, MSG* msg,struct sockaddr_in*cli)//转发
{
     int i = 0 ;
    for(i=0;i<MAX;i++)
    {
        if(1 == list[i].flag && 0!=memcmp(&list[i].cli,cli,sizeof(*cli)) )//确保有人,并且发送方和接收方的ip地址不一样
        {
            sendto(sockfd,msg,sizeof(MSG),0,(SA)&list[i].cli,sizeof(list[i].cli));
        }
    }
}
int do_logout(int sockfd, MSG* msg,struct sockaddr_in*cli)
{
    return 0;
}
int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }

    // man 7 ip 
    struct sockaddr_in ser,cli;
    bzero(&ser,sizeof(ser));
    bzero(&cli,sizeof(cli));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);
    ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    int ret = bind(sockfd,(SA)&ser,sizeof(ser));
    if(-1 == ret)
    {
        perror("bind");
        exit(1);
    }
    socklen_t len = sizeof(cli);
    MSG msg;
    while(1)
    {
        bzero(&msg,sizeof(msg));
        recvfrom(sockfd,&msg,sizeof(msg),0,(SA)&cli,&len);
        switch(msg.type)
        {
            case CMD_LOGIN:
                do_login(sockfd,&msg,&cli);
                break;
            case CMD_LOGOUT:
                do_logout(sockfd,&msg,&cli);
                break;
            case CMD_CHAT:
                do_chat(sockfd,&msg,&cli);
                break;
        
        }
    }
    close(sockfd);
    return 0;
}

客户端:(并行)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct 
  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值