Linux23 --- 三次握手四次挥手、客户端编程流程代码、命令netstat、 tcp协议是个面向链接的可靠的流式服务

tcp协议特点:
面向连接的,可靠的,流式服务。
在这里插入图片描述

一、三次握手 、四次挥手

链接的建立通过三次握手,链接的断开通过四次挥手

1、TCP固定头部结构

在这里插入图片描述
在这里插入图片描述

2、三次握手

在这里插入图片描述

3、四次挥手

在这里插入图片描述

二、命令 - netstat -natp

n - 用数字来表示ip地址、端口,而不是用服务名
a - 所有,结果包含监听套接字
t - 显示tcp链接
p - 显示进程的id号

在这里插入图片描述
在这里插入图片描述

三、tcp协议是个面向链接的可靠的流式服务:应答确认,超时重传。

丢了能重发,重复了能去掉。但可能出现先发的后到达的特殊情况,数据传输的路径可能不一样。但存在序号,会进行乱序重排。
在这里插入图片描述
在这里插入图片描述

如何看到发送缓存区还有多少个字节为发送,接收缓存区还有多少个字节未接收?

使用命令 netstat 可以看
netstat -natp

示例:将接收大小改为一字节

 int n = recv(c,buff,127,0);

 int n = recv(c,buff,1,0);

运行结果:
ok的次数会不一样,可以看出send和recv的次数是可以不一样的
在这里插入图片描述

四、客户端编程流程代码

在这里插入图片描述

1、循环进行数据收发

运行结果:

链接成功:
在这里插入图片描述
可以正常进行传输,客户端输入end结束链接
在这里插入图片描述

当多个客户端运行时,只有第一个运行的客户端能正常传输数据,第二个客户端已经链接,但不能正常发送数据,没有机会去接收链接

在这里插入图片描述
服务器端与客户端建立链接之后,套接字c1、c2和客户端sockfd,两边都存在接收和发送缓存区,当执行recv时,是从自身的接收缓存区接收数据,发送时从发送缓存区将数据发送到对方的接收缓存区。所以,send执行成功,只能说其成功将数据成功写入到发送缓存区,是否成功发送,不知道。当写入发送缓存区时,会根据相关底层协议的规定会将其发送到对方计算机描述符对应的接收缓存区中,具体操作根据相关协议要求。
在这里插入图片描述

命令 - netstat -natp

n - 用数字来表示ip地址、端口,而不是用服务名
a - 所有,结果包含监听套接字
t - 显示tcp链接
p - 显示进程的id号

在这里插入图片描述
在这里插入图片描述

tcp协议是个面向链接的可靠的流式服务:应答确认,超时重传。

丢了能重发,重复了能去掉。但可能出现先发的后到达的特殊情况,数据传输的路径可能不一样。但存在序号,会进行乱序重排。
在这里插入图片描述
在这里插入图片描述

如何看到发送缓存区还有多少个字节为发送,接收缓存区还有多少个字节未接收?

使用命令 netstat 可以看
netstat -natp

示例:将接收大小改为一字节

 int n = recv(c,buff,127,0);

 int n = recv(c,buff,1,0);

运行结果:
ok的次数会不一样,可以看出send和recv的次数是可以不一样的
在这里插入图片描述

代码

服务器端 ser.c

一次只能正常链接一个客户端

代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int socket_init();

int main()
{
    int sockfd = socket_init();
    if( sockfd == -1)
    {
        exit(0);
    }

    while( 1 )
    {
        struct sockaddr_in caddr;//记录客户端地址 ip port
        int len = sizeof(caddr);
        int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
        if( c < 0)
        {
            continue;
        }

        printf("accept c = %d\n",c);//accept运行时会阻塞,打印提示有人链接

        while( 1 )
        {
            char buff[128] = {0};
            int n = recv(c,buff,127,0);//也可以使用read,会阻塞

            if( n <= 0)//对方关闭了==,对方出错了<
            {
                break;
            }

            printf("recv = %s\n",buff);
            send(c,"ok",2,0);//也可以使用write
        }
        close(c);//关闭c
        printf("close\n");//表面有一个客户端链接了,并已经关闭链接
    }

    close(sockfd);
}

int socket_init()
{
    //创建一个在传输层使用tcp协议的一个套接字
    int sockfd = socket(AF_INET,SOCK_STREAM,0); //AF_INET --地址zhu,目前的固定的、服务类型 ---- tcp流式服务
    if(sockfd == -1)//创建失败
    {
        return -1;
    }

    //定义一个套接字地址,一个ipv4 专用的地址
    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);//网络字节 大端
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");//将字符串转成无符号整形

    //指定ip端口
    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if( res == -1)
    {
        printf("bind err\n");
        return -1;
    }

    //创建监听队列
    res = listen(sockfd,5);
    if( res == -1)
    {
        return -1;
    }
    
    return sockfd;

}

客户端 cli.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
    //和服务器端通讯
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if( sockfd == -1)
    {
        printf("socket err\n");
        exit(0);
    }

    //指定服务器的ip和端口
    struct sockaddr_in saddr;//定义一个套接字的地址,代表服务器的地址
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);//6000代表服务器的端口,系统随机分配自身的端口  1024以内属于知名端口,例如短号110等,只有管理员用户可使用  4096以内为保留端口  一般使用都锁使用4096以上
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    //发起链接
    //开始三次握手
    int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//如果失败,先检查参数,在考虑端口和ip是否与服务器端匹配,在考虑网络是否有问题,在检查服务器是否启动
    if( res == -1)
    {
        printf("connect failed\n");
        exit(0);
    } 

    //从键盘获取数据
    while(1)
    {
        printf("input : \n");
        char buff[128] = {0};
        fgets(buff,128,stdin);
        if( strncmp(buff,"end",3) == 0)
        {
            break;
        }
        
        //发送数据
        send(sockfd,buff,strlen(buff) - 1,0);
        memset(buff,0,128);//清空
        int n = recv(sockfd,buff,127,0);
        if( n <= 0)//服务器关闭
        {
            break;
        }
        printf("buff = %s\n",buff);
        
    }

    close(sockfd);


}

服务器端 thread.c

//利用线程,实现多个正常链接

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include<pthread.h>
//实现多个客户端链接

int socket_init();

void* fun(void* arg)
{
    int c = (int)arg;
    while(1)
    {
        char buff[128] = {0};
        int n = recv(c,buff,127,0);
        if( n <= 0)
        {
            break;
        }
        printf("buff(%d) = %s\n",c,buff);
        send(c,"ok",2,0);
    }
    close(c);
    printf("close\n");
}

int main()
{
    int sockfd = socket_init();
    if( sockfd == -1)
    {
        exit(0);
    }

    while( 1 )
    {
        struct sockaddr_in caddr;//记录客户端地址 ip port
        int len = sizeof(caddr);
        int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
        if( c < 0)
        {
            continue;
        }

        printf("accept c = %d\n",c);//accept运行时会阻塞,打印提示有人链接:
        //创建一个线程
        pthread_t id;
        pthread_create(&id,NULL,fun,(void*)c);
        //简便的做法,但系统会给警告,整形和指针大小都是4之节,后面再转成整形,这将其看成指针
          
    }
    close(sockfd);
}

int socket_init()
{
    //创建一个在传输层使用tcp协议的一个套接字
    int sockfd = socket(AF_INET,SOCK_STREAM,0); //AF_INET --地址zhu,目前的固定的、服务类型 ---- tcp流式服务
    if(sockfd == -1)//创建失败
    {
        return -1;
    }

    //定义一个套接字地址,一个ipv4 专用的地址
    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;

    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");//将字符串转成无符号整形

    //指定ip端口
    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if( res == -1)
    {
        printf("bind err\n");
        return -1;
    }

    //创建监听队列
    res = listen(sockfd,5);
    if( res == -1)
    {
        return -1;
    }
    
    return sockfd;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
网络令主要用于网络诊断和测试,以及获取网络信息。常见的网络命令有ping、tracert、nslookup、netstat等。下面简单介绍一下这些命令的使用方法: 1. ping:用于测试网络连接是否通畅,常用于测试网络延迟和丢包情况。使用方法为在命令行中输入“ping 目标IP地址或域名”,例如“ping www.baidu.com”。 2. tracert:用于跟踪数据包在网络上传输的路径,以及检测网络延迟和丢包情况。使用方法为在命令行中输入“tracert 目标IP地址或域名”,例如“tracert www.baidu.com”。 3. nslookup:用于查询域名解析结果,以及测试DNS服务器是否正常工作。使用方法为在命令行中输入“nslookup 域名”,例如“nslookup www.baidu.com”。 4. netstat:用于显示当前网络连接状态和活动进程的网络统计信息。使用方法为在命令行中输入“netstat -a”,可以显示所有活动连接的信息。 至于TCP协议三次握手,它是TCP在建立连接时使用的一种机制,用于确保通信双方的状态同步。具体步骤如下: 1. 客户端服务器发送SYN报文,表示请求连接。 2. 服务器收到请求后回复一个SYN+ACK报文,表示确认请求并请求连接。 3. 客户端收到服务器的回复后发送一个ACK报文,表示确认连接建立。 这样,连接就建立成功了,双方可以开始通信了。三次握手的目的是为了防止已失效的连接请求报文段突然又传到了服务器,从而导致资源的浪费和混乱。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值