【Linux Day15 TCP网络通讯】

这篇具有很好参考价值的文章主要介绍了【Linux Day15 TCP网络通讯】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

TCP网络通讯

TCP编程流程

【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

接口介绍

  • socket()方法是用来创建一个套接字,有了套接字就可以通过网络进行数据的收发。创建套接字时要指定使用的服务类型,使用 TCP 协议选择流式服务(SOCK_STREAM)。

  • **bind()方法是用来指定套接字使用的 IP 地址和端口。**IP 地址就是自己主机的地址,测试程序时可以使用回环地址“127.0.0.1”。端口是一个 16 位的整形值,一般 0-1024 为知名端口,如 HTTP 使用的 80 号端口。这类端口一般用户不能随便使用。其次,1024-4096 为保留端口,用户一般也不使用。4096 以上为临时端口,用户可以使用。在Linux 上,1024 以内的端口号,只有 root 用户可以使用。

  • **listen()方法是用来创建监听队列。**监听队列有两种,一个是存放未完成三次握手的连接,一种是存放已完成三次握手的连接。listen()第二个参数就是指定已完成三次握手队列的长度。

    【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

  • accept()处理存放在 listen 创建的已完成三次握手的队列中的连接。每处理一个连接,则accept()返回该连接对应的套接字描述符。如果该队列为空,则 accept 阻塞。

  • connect()方法一般由客户端程序执行,需要指定连接的服务器端的 IP 地址和端口。该方法执行后,会进行三次握手, 建立连接。

  • send()方法用来向 TCP 连接的对端发送数据。send()执行成功,只能说明将数据成功写入到发送端的发送缓冲区中,并不能说明数据已经发送到了对端。send()的返回值为实际写入到发送缓冲区中的数据长度。

  • recv()方法用来接收 TCP 连接的对端发送来的数据。recv()从本端的接收缓冲区中读取数据,如果接收缓冲区中没有数据,则 recv()方法会阻塞;返回值是实际读到的字节数,如果recv()返回值为 0, 说明对方已经关闭了 TCP 连接。

  • close()方法用来关闭 TCP 连接。此时,会进行四次挥手

客户端代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    assert(sockfd != -1);
    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");

    int res = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
    if (-1 == res)
    { 
        exit(1);
    }
    while (1)
    {
        char buff[128] = {0};
        printf("input:\n");
        fgets(buff, 128, stdin);
        if (strncmp(buff, "end", 3) == 0)
        {
            break;
        }
        send(sockfd, buff, strlen(buff), 0);
        memset(buff, 0, 128);
        recv(sockfd, buff, 127, 0);
        printf("buff=%s\n", buff);
    }
    close(sockfd);
    exit(0);
}


服务端代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd)
    {
		exit(1);
    }
    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);                   // htons 将主机字节序转换为网络字节
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 回环地址
    int res = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
    if (-1 == res)
    {
        exit(1);
    }
    res = listen(sockfd, 5);
    if (-1 == res)
    {
        exit(1);
    }
    struct sockaddr_in caddr;
    socklen_t len = sizeof(caddr);
    int n = 0;
    int c = -1;
    while (1) // 服务器循环接收客户端连接
    {
        char data[128] = {0};
        if (n == 0)
        {
            c = accept(sockfd, (struct sockaddr *)&caddr, &len); // 阻塞
            if (c == -1)
            {
                printf("accept error ");
                continue;
                ;
            }
        }
        n = recv(c, data, 127, 0); // 阻塞
        if (n == 0)                //连接关闭
        {
            close(c);
            printf("client close\n");
            continue;
        }
        else if (n < 0)            //出错
        {
            printf("recv error");
            continue;
        }
        printf("n = %d, buff = %s\n", n, data);
        send(c, "OK", 2, 0);
    }
    close(sockfd); 
    exit(0);
}

运行结果:

【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

引入多线程处理并发

服务器端代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

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

int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        exit(1);
    }
    struct sockaddr_in saddr, caddr;
    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");

    int res = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
    if (-1 == res)
    {
        exit(1);
    }
    listen(sockfd, 10);
    while (1)
    {
        int len = sizeof(caddr);
        int c = accept(sockfd, (struct sockaddr *)&caddr, &len);
        if (c < 0)
        {
            continue;
        }
        printf("accept c = %d\n", c);
        pthread_t id;
        pthread_create(&id, NULL, run, (void *)c);
    }
    close(sockfd);
    exit(0);
}

运行结果:
【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

引入fork处理并发

服务器端代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

void DealClientLink(int c, struct sockaddr_in caddr)
{
    while (1)
    {
        char buff[128] = {0};
        int n = recv(c, buff, 127, 0);
        if (n <= 0)
        {
            break;
        }
        printf("%s:%d %s", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buff);
        send(c, "OK", 2, 0);
    }
    printf("One Client Close\n");
    close(c);
}

void Signal_Fun(int sign)
{
    wait(NULL);
}
int main()
{
    signal(SIGCHLD, Signal_Fun); // 用wait()处理僵死进程
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        printf("create sockfd error\n");
        exit(1);
    }
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    int res = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
    assert(-1 != res);

    listen(sockfd, 10);

    while (1)
    {
        struct sockaddr_in caddr;
        int len = sizeof(caddr);
        int c = accept(sockfd, (struct sockaddr *)&caddr, &len);
        assert(-1 != c);
        printf("%s:%d Link Success\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
        pid_t pid = fork();
        if (-1 == pid)
        {
            exit(1);
        }
        if (0 == pid)
        {
            DealClientLink(c,caddr);
            exit(0);   //必须结束子进程,否则会有多个进程调 accept
        }
        else
        {
            close(c);  //父子进程都需要关闭 c
        }
    }
    close(sockfd);
    exit(0);
}

运行结果: 【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

TCP连接状态转变图

【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

三次握手

  • 流程图

【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

  • 使用netstat工具查看状态变化(参考图3-8)

    【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

四次挥手

  • 流程图

    【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

  • 使用netstat命令查看状态(参考图3-8)

    【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

TIME_WAIT的作用

在图3-8中,当客户端连接在收到服务器的结束报文段之后,并没有直接进人CLOSED 状态,而是转移到 TIME_WAIT 状态。在这个状态,客户端连接要等待段长为2MSL(Maximum Segment Life,报文段最大生存时间)的时间,才能完全关闭;MSL是 TCP 报文段在网络中的最大生存时间,标准文档 RFC 1122 的建议值是2 min;

TIME WAIT 状态存在的原因有两点:
  1. 可靠地终止TCP 连接

    当服务器发给客户端的ACK中途丢失,客户端收不到ACK,会重新发送FIN,如果此时服务器已经关闭,无法接收来自客户端的FIN,便会陷入一种“藕断丝连”状态(一方关闭,一方未关闭)。这显然是不合适的,因为TCP 连接是全双工的,双方完成数据交换之后,通信双方都必须断开连接以释放系统资源

  2. 保证让迟来的TCP 报文段有足够的时间被识别并丢弃

    在 Linux 系统上,一个TCP 端口不能被同时打开多次(两次及以上)。当一个TCP 连接处于 TIME_WAIT 状态时,我们将无法立即使用该连接占用着的端口来建立一个新连接。反过来,如果不存在 TIME WAIT 态,则应用序能够立即建立一个和刚关闭的连接相似的连接(这里说的相似,是指它们具有相同的 IP 地址和端口号)。这个新的、和原来相似的连接被称为原来的连接的化身 (incarmation)。新的化身可能接收到属于原来的连接的、携带应用程序数据的 TCP 报文段(迟到的报文段),这显然是不应该发生的。这就是 TIMEWAIT 状态存在的第二个原因。

TCP协议特点

流式服务

TCP 字节流的特点,发送端执行的写操作次数和接收端执行的读操作次数之间没有任何数量关系,应用程序对数据的发送和接收是没有边界限制的。如下图:

【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

TCP连接的可靠性

  • IPV4报文格式:

【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

  • TCP报文格式:

【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

  • 应答机制

【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

  • 超时重传

【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络

TCP 传输是可靠的。首先,TCP 协议采用发送应答机制,即发送端发送的每个 TCP 报文段都必须得到接收方的应答,才认为这个 TCP 报文段传输成功。其次,TCP 协议采用超时重传机制,发送端在发送出1个 TCP 报文段之后启动定时器,如果在定时时间内未收到应答,它将重发该报文段。最后,因为 TCP 报文段最终是以 IP数据报发送的,而 数据报到达接收端可能乱序、重复,所以 TCP 协议还会对接收到的 TCP 报文段重排、整理,再交付给应用层。

粘包问题

在流式服务中如上图3-9所示,尽管报文已经按顺序整理好并接受,但是无法分割成正确的信息,就形成了所谓的粘包问题,为了解决此问题,我们可以每次发送时进行标记分割,以便于接收方进行分析和拆分,如下图:

【Linux Day15 TCP网络通讯】,Linux学习,linux,tcp/ip,网络文章来源地址https://www.toymoban.com/news/detail-827278.html

到了这里,关于【Linux Day15 TCP网络通讯】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 关于ROS的网络通讯方式TCP/UDP

    ROS 系列学习教程(总目录) TCP/IP协议族为传输层指明了两个协议:TCP和UDP,它们都是作为应同程序和网络操作的中介物。 TCP(Transmission Control Protocol)协议全称是传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC793定义。 TCP是面向连接的、

    2024年02月05日
    浏览(29)
  • Java与Modbus-TCP/IP网络通讯

    通过Java与Modbus-TCP/IP网络通讯实现举例5中的功能

    2024年02月10日
    浏览(36)
  • Week 08-day02-Unity网络通讯之聊天室

    简单UI拖拽一下:  聊天室实现步骤: 1.向服务器发送消息 2.刷新Content聊天界面 3.清空输入框  代码: chatPanel: NetManager: 加上服务器运行截图: 代码: 动态链接库:ProtocolConfig: 动态链接库:ProtocolEnum: 动态链接库:SocketMessage: Main函数: NetManager: ClientPeer类: ClientMessage类:

    2024年02月06日
    浏览(46)
  • 网络编程——基于TCP协议的通讯录【课程设计】

    题目:基于TCP协议的通讯录 设计目标: (1)了解Socket通信的原理,在此基础上编写一个基于TCP协议的通讯录; (2)理解TCP通信原理; 课程设计系统组成及模块功能: 此TCP/IP课程设计实现了基于TCP的客户/服务器通信程序,需要实现以下一些基本功能: (1)客户端连接服务

    2024年02月06日
    浏览(60)
  • 【计算机网络】 基于TCP的简单通讯(客户端)

    加载库 创建套接字 连接服务端 连接服务端我们使用的是connect()函数,分别为连接使用的socket,连接的地址信息,连接的地址信息长度。返回值为int类型,如果没有错误返回0,否则返回SOCKET_ERROR。 收发数据 关闭套接字、卸载库 这样我们的TCP协议简单通讯就写好了。 先运行

    2024年02月07日
    浏览(44)
  • Unity3D TCP网络通讯核心意涵与基本原理详解

    在Unity3D中,TCP网络通讯是一种常用的通讯方式,它可以实现可靠的数据传输和连接。 对惹,这里有一 个游戏开发交流小组 ,希望大家可以点击进来一起交流一下开发经验呀 本文将详细介绍Unity3D TCP网络通讯的核心意涵与基本原理,包括技术详解和代码实现。 一、TCP网络通

    2024年03月12日
    浏览(36)
  • 【网络】TCP通讯(三次握手、四次挥手;滑动窗口;TCP状态转换;端口复用;TCP心跳检测机制)

     前言:建议看着图片,根据文字描述走一遍TCP通讯过程,加深理解。 目录 TCP通信时序: 1)建立连接(三次握手)的过程: 2)数据传输的过程: 3)关闭连接(四次挥手)的过程: 滑动窗口 (TCP流量控制): TCP状态转换: 半关闭: 2MSL: 程序设计中的问题: 端口复用:

    2024年02月07日
    浏览(42)
  • 【Java基础教程】(四十七)网络编程篇:网络通讯概念,TCP、UDP协议,Socket与ServerSocket类使用实践与应用场景~

    了解多线程与网络编程的操作关系; 了解网络程序开发的主要模式; 了解 TCP 程序的基本实现; 在Java中,网络编程的核心意义是实现不同电脑主机之间的数据交互。Java采用了一种简化的概念,将这个过程进一步抽象为JVM(Java虚拟机)进程之间的通信。可以在同一台电脑上

    2024年02月15日
    浏览(39)
  • esp8266WIFI模块教程:ATK-ESP8266——TCP网络通讯和服务器连接

      ATK-MW8266D 模块是正点原子推出的一款高性能 UART-WIFI(串口-无线)模块,ATK-MW8266D 模块板载了正点原子公司自主研发的 ATK-ESP-01 模块,该模块通过 FCC、CE 认证,可直接用于出口欧美地区的产品。   ATK-MW8266D 模块采用串口(LVTTL)与 MCU(或其他串口设备)通讯,且内置

    2024年04月15日
    浏览(37)
  • 物联网网络通讯知识

    RTU英文全称Remote Terminal Units,中文全称为远程终端单元。远程终端设备(RTU)是安装在远程现场的 电子设备 ,用来监视和测量安装在远程现场的传感器和设备。通俗理解就是能够编程的还可以将数据传输到服务器的工具。RTU内部是包含 通讯模块 的,RTU仪表配置服务器后,就可

    2024年02月05日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包