网络编程复习

 1.网络编程基础

  1.1引入

socket套接字实现主机之间的通信
cs通信模型基于socket实现,需要客户端软件来实现通信
bs通信模型基于http实现,是网页通信,不需要任何客户端软件

1.2通信协议

(1)OSI七层通信协议:物理层,链路层,网络层,运输层,会话层,表示层,应用层
(2)TCP/IP四层通信协议:网络接口层,网际层,运输层,应用层
(3)五层协议体系: 物理层,数据链路层,网络层,运输层,应用层

分层的好处:各层之间功能独立,每层的功能不需要通过上层或者下层间接得到,上层下层发生变化,不会影响这一层的功能,便于根据每层功能对每层进行查错和维护,促进标准化工作,结构上不可分割。
OSI和TCP/IP每层对比:

数据头:封包和拆包过程:

linux默认封装MTU一帧数据最大为1500字节。

1.3TCP和UDP区别

1.tcp面向有链接传输,有三次握手四次挥手机制,会给每个包编上序列号避免乱序,拥有确认包机制,有超时重传机制,有接收缓存区和发送缓存区,为了提高效率,会将多个小包数据沾成一个包发送,这种现象叫做粘包现象,具有网络拥堵检测,实时性较差,数据传输更稳定,不易造成数据丢失。
2.udp面向无连接传输,没有发送缓存区,有接收缓存区,尽最大努力传输,接收不及时会造成丢包,超过最大发送的数据大小会删掉多出的部分,实时性强,传输效率高,数据容易丢失乱序。

1.4字节序

网络字节序采用大端存储,数据低位存高地址,数据高位存低地址。

ip地址:网络号(三个字节)+主机号(一个字节),共四个字节,如:255.255.255.0
ip地址字节序转换函数:
       将主机字节序转为网络字节序: inet_addr(const char*)

        将网络字节序转为主机字节序:inet_ntoa(const char*)
端口号:占用两个字节,如:8888

端口号字节序转换函数:

        将两字节无符号整数的主机字节序转为网络字节序:htons(uint16_t hostshort)

        将两字节无符号整数的网络字节序转为主机字节序:ntohs(uint16_t hostshort)

1.5子网掩码

子网掩码是对主机号的二次划分,子网掩码将原ip划分为多个网段,每个网段绑定一定数量的子网ip地址。子网掩码会将一字节的主机号划分为四字节子网掩码:三字节网络号和一字节主机号,网络号全由1组成,主机号全由0组成。网络号可以向主机号借子网号,借来的子网号的值可以改变,因此可以将原ip划分为多个网段。C类网络默认子网掩码是255.255.255.0
子网地址 = 原ip地址&多个子网掩码(子网掩码个数取决于从主机号借用的可变的子网号的个数)

两个特殊的ip:

        主机号全为0:标识网络的起始地址,主机不可用

        主机号全为1:全网广播地址,主机不可用

划分后最大的网段个数:2^(子网中1的个数)
划分后最大可用的主机个数:2^(子网中0的个数)-2

1.6端口号

TCP和UDP的端口号是相互独立的

可以使用的:1024~49151,就是我们平时编写服务器使用的端口号

临时端口号:49152~65535,这部分是客户端运行时候动态选择的

1.7域名

一个域名可以绑定多个ip,域名ip可通过shell指令查看:ping www.baidu.com

展示:ubuntu@ubuntu:~/test$ ping www.baidu.com
PING www.a.shifen.com (180.101.50.188) 56(84) bytes of data.
64 字节,来自 180.101.50.188 (180.101.50.188): icmp_seq=1 ttl=128 时间=8.91 毫秒
64 字节,来自 180.101.50.188 (180.101.50.188): icmp_seq=2 ttl=128 时间=13.2 毫秒
ubuntu@ubuntu:~/test$ ping www.baidu.com
PING www.a.shifen.com (180.101.50.242) 56(84) bytes of data.
64 字节,来自 180.101.50.242 (180.101.50.242): icmp_seq=1 ttl=128 时间=22.5 毫秒
64 字节,来自 180.101.50.242 (180.101.50.242): icmp_seq=2 ttl=128 时间=11.0 毫秒
通过上面的结果,可以看到百度官网的域名绑定了多个ip地址

域名结构:

        例如域名 http: //www.baidu.com.cn 从右向左看

        cn为高级域名,也叫一级域名,它通常分配给主干节点,取值为国家名,cn代表中国

        com为网络名,属于二级域名,它通常表示组织或部门

        中国互联网二级域名共40个,edu表示教育部门,com表示商业部门,gov表示政府,军队mil

        baidu为机构名,在此为三级域名,表示百度

        www:万维网world wide web,也叫环球信息网,是一种特殊的信息结构框架。

        http:使用的是超文本传输协议

2.tcp和udp通信

 1.套接字

套接字socket,通信的载体,使用socket函数创建通信节点,返回该端点的文件描述符,端点中包含发送缓存区和接收缓存区。

2.tcp通信程序

1.服务器端

//实现的功能描述:tcp并发服务器,服务器端可以发送数据给所有连接中的客户端
//实现的功能描述:tcp并发服务器,服务器端可以发送数据给所有连接中的客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <netinet/ip.h> /* superset of previous */
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
// #define SER_PORT 8888              //服务器端口号
// #define SER_IP "192.168.125.119"   //服务器ip地址
int savefd[128] = {};
pthread_mutex_t mutex;
int is_reading = 0;
typedef struct
{
    int newfd;
    struct sockaddr_in cin;
}info;

void *run(void *arg)
{
    struct epoll_event ev;
    struct epoll_event events[128];
    int flags;
    int ret;
    int i,j;
    int newfd;
    newfd=((info *)arg)->newfd;
    struct sockaddr_in cin = ((info*)arg)->cin;
    //5、与客户端进行相互通信
    char rbuf[128] = "";           //读取消息内容的容器
    int efd = epoll_create(10);
    if(efd < 0)
    {
        perror("efd is err:");
        exit(1);
    }
    ev.data.fd = newfd;
    ev.events = EPOLLIN;
    epoll_ctl(efd, EPOLL_CTL_ADD, newfd, &ev);
    ev.data.fd = 0;
    ev.events = EPOLLIN;
    epoll_ctl(efd, EPOLL_CTL_ADD, 0, &ev);
    while(1)
    {
        
        
        //清空容器
        bzero(rbuf, sizeof(rbuf));
        ret = epoll_wait(efd, events, 128, -1);
       
        if(ret<0)
        {
            perror("wait error");

        }
        for(i=0; i<ret; i++)
        {
            if(events[i].data.fd == 0)
            {
                
                //将收到的消息处理一下,回复给客户端
                // strcat(rbuf, "*_*");
                // 尝试获取锁以读取输入  
                if (pthread_mutex_trylock(&mutex) == 0) {  
                    // 成功获取锁,设置标志位  
                    is_reading = 1;  
                    scanf("%s", rbuf);
                   
                    while(getchar()!=10);
                    
                    // 读取完成,释放锁并清除标志位  
                    is_reading = 0;  
                    pthread_mutex_unlock(&mutex);  
                }
                else{//沒有獲取到鎖就跳過
                    printf("Thread skipped reading input.\n");
                }
                //讲消息发送给客户端
                //write(newfd, rbuf, strlen(rbuf));
                for(j=0;j<128;j++)
                {
                    if(savefd[j] != 0){
                        send(savefd[j], rbuf, strlen(rbuf), 0);
                    }
                }
                printf("发送成功\n");
               
            }
            
            if(events[i].data.fd == newfd)
            {
                
                //从套接字中读取数据
                //int res = read(newfd, rbuf, sizeof(rbuf));
                int res = recv(newfd, rbuf, sizeof(rbuf), 0);
                if(res == 0)
                {
                    epoll_ctl(efd, EPOLL_CTL_DEL, newfd, &ev);
                    // close(newfd);
                    printf("客户端已经下线\n");   
                    close(newfd);
                    pthread_exit(NULL);
                }
                
                //将读取的消息展示出来
                printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), rbuf);
                
            }
           
        }
    }    
    close(newfd);
    pthread_exit(NULL);
}

int main(int argc, const char *argv[])
{
    int pfd;
    int i=0;
    pthread_t thread;
    //1、为通信创建一个端点
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    //参数1:说明使用的是ipv4通信域
    //参数2:说明使用的是TCP面向连接的通信方式
    //参数3:由于参数2中已经指定通信方式,填0即可
    if(sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("socket success sfd = %d\n", sfd);   //3
    //调用端口号快速重用函数
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
    {
        perror("setsockopt error");
        return -1;
    }
    //2、绑定ip和端口号
    //2.1 准备地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;    //通信域
    sin.sin_port = htons(atoi(argv[2]));    //端口号
    sin.sin_addr.s_addr = inet_addr(argv[1]);  //ip地址
    //2.2 绑定工作
    if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) ==-1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");
    //3、将套接字设置成被动监听状态
    if(listen(sfd, 128)==-1)
    {
        perror("listen error");
        return -1;
    }
    printf("listen success\n");
    //4、阻塞等待客户端的连接
    //4.1 定义用于接受客户端信息的容器
    struct sockaddr_in cin;
    socklen_t addrlen = sizeof(cin);
    while(1)
    {
        
        savefd[i] = accept(sfd, (struct sockaddr*)&cin, &addrlen);
        if(savefd[i] == -1)
        {
            perror("accept error");
            return -1;
        }
        printf("[%s:%d]:发来连接请求\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
        info buf;
        buf.newfd = savefd[i];
        buf.cin = cin;
        pfd = pthread_create(&thread, 0, run, &buf);
        if(pfd < 0)
        {
            perror("pthread is err:");
            return -1;
        }
        i++;
        pthread_detach(thread);
        if (i >= 128) {  
            perror("Too many connections");  
            
            exit(1);  
        }
        
    }
    //6、关闭套接字
    close(sfd);
    //销毁互斥锁
    pthread_mutex_destroy(&mutex);
    return 0;
}

2.客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <netinet/ip.h> /* superset of previous */
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
// #define SER_PORT 8888
// #define SER_IP "192.168.125.119"
// #define CLI_PORT 6666
// #define CLI_IP "192.168.250.119"
void handler(int signum)
{
   
    if(signum == SIGCHLD)
    {
        while(waitpid(-1, 0, WNOHANG) != 0);

    }
}
int main(int argc, const char *argv[])
{
    
    //1、创建用于通信的套接字文件描述符
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if(cfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("cfd = %d\n", cfd);            //3
    
    //2、绑定IP地址和端口号 ?
    //2.1 填充客户端地址信息结构体
    // struct sockaddr_in cin;
    // cin.sin_family = AF_INET;
    // cin.sin_port = htons(CLI_PORT);
    // cin.sin_addr.s_addr = inet_addr(CLI_IP);
    // //2.2 绑定
    // if( bind(cfd, (struct sockaddr*)&cin, sizeof(cin)) ==-1)
    // {
    //     perror("bind error");
    //     return -1;
    // }
    // printf("bind success\n");
    
    
    //3、连接服务器
    //3.1 准备对端地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(atoi(argv[2]));
    sin.sin_addr.s_addr = inet_addr(argv[1]);
    //3.2 连接服务器
    if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin))==-1)
    {
        perror("connect error");
        return -1;
    }
    printf("connect success\n");
    
    //4、数据收发
    char wbuf[128] = "";
    char rbuf[128] = "";
    int ret = fork();
    if(signal(SIGCHLD,handler) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
    while(1)
    {
        if(ret > 0)
        {
            //从终端上获取要发送的数据
            fgets(wbuf, sizeof(wbuf), stdin);
            wbuf[strlen(wbuf)-1] = 0;

            //将数据发送给服务器
            send(cfd, wbuf, strlen(wbuf), 0);
            printf("发送成功\n");
        }
        else
        {
            //接收服务器发来的消息
            bzero(rbuf, sizeof(rbuf));

            recv(cfd, rbuf, sizeof(rbuf), 0);
            printf("服务器发来的消息为:%s\n", rbuf);
        }
    }
    
    //5、关闭套接字
    close(cfd);

    return 0;
}

3.udp通信程序

1.服务器端

2.客户端

3.广播和组播

1.广播

2.组播

4.流式域套接字和报式域套接字

用于本地通信。

5.超时检测

6.抓包分析

7.数据库

8.静态库动态库

  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值