精华内容
下载资源
问答
  • TCP通信原理

    千次阅读 2018-09-29 17:02:59
    首先,对于TCP通信来说,每个TCP Socket的内核中都有一个发送缓冲区和一个接收缓冲区,TCP的全双工的工作模式及TCP的滑动窗口就是依赖于这两个独立的Buffer和该Buffer的填充状态。 接收缓冲区把数据缓存到内核,若...

    首先,对于TCP通信来说,每个TCP Socket的内核中都有一个发送缓冲区和一个接收缓冲区,TCP的全双工的工作模式及TCP的滑动窗口就是依赖于这两个独立的Buffer和该Buffer的填充状态。

    接收缓冲区把数据缓存到内核,若应用进程一直没有调用Socket的read方法进行读取,那么该数据会一直被缓存在接收缓冲区内。不管进程是否读取Socket,对端发来的数据都会经过内核接收并缓存到Socket的内核接收缓冲区。

    read索要做的工作,就是把内核接收缓冲区中的数据复制到应用层用户的Buffer里。

     

    进程调用Socket的send发送数据的时候,一般情况下是讲数据从应用层用户的Buffer里复制到Socket的内核发送缓冲区,然后send就会在上层返回。换句话说,send返回时,数据不一定会被发送到对端。

     

     

     

     

     

    什么是滑动窗口协议

    发送方和接收方都会维护一个数据帧的序列,这个序列被称作窗口。发送方的窗口大小由接收方确认,目的是控制发送速度,以免接收方的缓存不够大导致溢出,同时控制流量也可以避免网络拥塞。

    下面图中的4,5,6号数据帧已经被发送出去,但是未收到关联的ACK,7,8,9帧则是等待发送。可以看出发送端的窗口大小为6,这是由接受端告知的(事实上必须考虑拥塞窗口cwnd,这里暂且考虑cwnd>rwnd)。此时如果发送端收到4号ACK,则窗口的左边缘向右收缩,窗口的右边缘则向右扩展,此时窗口就向前“滑动了”,即数据帧10也可以被发送

     

     

    明白了Socket读写数据的底层原理,我们就很容易理解“阻塞模式”:对于读取Socket数据的过程而言,如果接收缓冲区为空,则调用Socket的read方法的线程会阻塞,直到有数据进入接收缓冲区;而对于写数据到Socket中的线程来说,如果待发送的数据长度大于发送缓冲区空余长度,则会阻塞在write方法上,等待发送缓冲区的报文被发送到网络上,然后继续发送下一段数据,循环上述过程直到数据都被写入到发送缓冲区为止

     

    从前面分析的过程来看,传统的Socket阻塞模式直接导致每个Socket都必须绑定一个线程来操作数据,参与通信的任意一方如果处理数据的速度较慢,会直接拖累到另一方,导致另一方的线程不得不浪费大量的时间在I/O等待上,所以这就是Socket阻塞模式的“缺陷”。但是这种模式在少量的TCP连接通信的情况下,双方都可以快速的传输数据,这个时候的性能是最高的。

    展开全文
  • TCP 通信原理

    2016-03-30 22:51:00
    2019独角兽企业重金招聘Python工程师标准>>> 第一、服务端代码模型: ...小结:Java TCP网络编程注意事项: 转载于:https://my.oschina.net/ukapollo/blog/650275

    第一、服务端代码模型:

    224841_Qx5C_219809.jpg

    第二、客户端代码模型:

    224855_1aUp_219809.jpg


    小结:Java TCP网络编程注意事项:

    224909_WTb7_219809.jpg



    转载于:https://my.oschina.net/ukapollo/blog/650275

    展开全文
  • 清晰易懂TCP通信原理解析(附demo、简易TCP通信库源码、解决沾包问题等)C#版

    清晰易懂TCP通信原理解析(附demo、简易TCP通信库源码、解决沾包问题等)C#版

    参考文章:

    (1)清晰易懂TCP通信原理解析(附demo、简易TCP通信库源码、解决沾包问题等)C#版

    (2)https://www.cnblogs.com/xiaozhi_5638/p/4244797.html


    备忘一下。


    展开全文
  • 浅析TCP通信原理

    万次阅读 2017-09-10 20:08:37
    TCP通信 TCP是面向连接的通信,所以在通信之前,客户端与服务器端必须通过三次握手建立连接,然后在通信完毕,还要通过四次挥手断开连接。 (一)相关函数 1.创建套接字 domain:地址类型,ipv4、ipv6、...
    TCP通信
    TCP是面向连接的通信,所以在通信之前,客户端与服务器端必须通过三次握手建立连接,然后在通信完毕,还要通过四次挥手断开连接。
    (一)相关函数
    1.创建套接字

    domain:地址类型,ipv4ipv6unix的地址类型分别定义为常数AF_INETAF_INET6AF_UNIX.
    type:socket传输类型,tcp通信是面向字节流的,所以为SOCK_STREAM

    在网络通信时,我们的数据要从本主机通过网络发送到对端主机,数据在内存中存放的形式有大端或者小端两种形式,所以在向网络中传输数据是,网络就要按照一定的规定收发数据。TCP/IP协议规定,网络字节流应按照大端字节流,即低地址高字节。
    网络数据流的地址规定:先发出的数据是低地址,后发出的数据是高低址,因为网络字节流为大端,也就是先发送数据的高位字节,在发送低位字节。

    2.为了代码的可移植性,下面库函数为实现网络字节序列到主机字节序列的转换。

    h = host主机
    n = network网络
    l = 长整形
    s = 短整形
    htonl代表主机字节序转换成网络字节序

    3.将套接字与socket结构体绑定,socket结构体会指定ip,端口号,还有地址类型

    一般服务器的端口号和ip是绑定的,众所周知的,而客户端的端口号可以随机分配的
    IPV4 struct sockaddr_in 结构体的结构
    IP地址+端口就称为socket,所以socket的结构体内包含有ip 和端口号
    struct in_addr {
    __be32 s_addr;
    };
    struct sockaddr_in {
    sa_family_t sin_family; /* Address family */
    __be16 sin_port; /* Port number */
    struct in_addr sin_addr; /* Internet address */
    /* Pad to size of `struct sockaddr'. */
    unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
    sizeof(unsigned short int) - sizeof(struct in_addr)];
    };
    sin_family:地址类型,ipv4ipv6unix的地址类型分别定义为常数AF_INETAF_INET6AF_UNIX.
    地址类型的作用:因为在不同的环境下socket的数据结构不同,ipv4使用的struct sockaddr_in,而ipv6使用的是struct sockaddr_in6。但在网络编程里的许多函数需要传参sockaddr结构体,Struct sockaddr结构体类型就如同void*类型,它可以接受任意类型的结构体,所以在传参时就不需要知道具体的socket结构体类型,可以给根据地址类型来确定结构体的内容。
    Sockeaddr数据结构

    4.将sockfd设置为监听套接字,并通过参数2是指明最多可以监听多少个套接字。

    监听套接字的作用:server服务器启动后,会源源不断的有客户端来连接,这时候就需要一个监听套接字,来把一个个来访的socket按顺序存起来,并按顺序交给accept的去处理并返回newsockfd去收发数据,这样做可以保证在server满负荷的处理其他socket时,其他客户端要访问服务器时,可以通过监听队列等待一会,有其他客户端断开连接了,他就可以连接了。

    5.

    阻塞式等待客户端连接,监听套接字一直在监听是否有新连接到来连接,如果有链接则接受对方连接,连接之后由返回值new_sock收发数据
    new_sock(1) = accept(监听套接字(2),struct socket_in输出型参数(3),输入输出型参数(4))
    6.

    client不需要被别人连接,只需要连接别人,所以使用connect来连接服务器

    (二)TCP通信的基本原理

     
    三次握手建立连接:server端调用socket(),bind(),listen()创建监听套接字并完成初始化,然后调用accept()阻塞式等待客户连接。客户端创建一个套接字初始化后,调用connect连接server,连接过程:调用connect()发出SYN段并阻塞等待服务器应答(client:我想要连接你),服务器应答一个SYN-ACK(server:好,我准备好了,你连接吧),客户端收到从connect()返回,同时应答一个ACK给server(client:太好了,我连接好了),服务器收到ACK,从accept返回。

    数据传输过程:建立连接后,TCP可以提供全双工的通信,server先读再写,client先写在读,用read()和write()阻塞式的等待一个写一个读。一直循环下去。

    四次挥手关闭连接:假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,"告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息"。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,"告诉Client端,好了,我这边数据发完了,准备好关闭连接了"。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,"就知道可以断开连接了"。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!如果一方调用shutdown()则连接处于半关闭状态,仍可接受对方的数据。

    (三)代码实现
    server.c
    clude<stdio.h>
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    #include<string.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    
    #define _BACKLOG_ 10
    
    int GetSocket(int port)
    {
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if(sock<0){
            perror("socket");
            exit(1);
        }
        printf("%d:socket create is ok\n", sock);
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        server.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY这个宏代表本地的任意ip地址,
                                                                                    //因为本地可能多个网卡。这个宏的值为0
        if(bind(sock, (struct sockaddr*)&server,sizeof(server))<0){
            perror("bind");
            close(sock);
            exit(2);
        }
        printf("bind is ok\n");
        if(listen(sock, _BACKLOG_)< 0){
            perror("listen");
            close(sock);
            return 3;
        }
        printf("listen is ok\n");
        return sock;
    }
    
    
    void use(char *a)
    {
        printf("#%s [port_server]\n", a);
    }
    int main(int argc, char *argv[])
    {
        printf("main start\n");
        if(argc<2)
        {
           use(argv[0]);
           return 6;
        }
        printf("use is ok\n");
        int listen_sock = GetSocket(atoi(argv[1])); 
        
        printf("GetSocket is ok\n");
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        
        printf("wait accept....\n");
        while(1)
        {
            int new_sock = accept(listen_sock,(struct sockaddr*)&client, &len);
            if(new_sock< 0)
            {
                perror("accept");
                close(new_sock);
                return 4;
            }
            printf("[%s][%d]:accept is ok\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
            pid_t pid = fork();
            if(pid < 0){
                close(new_sock);
                printf("process creation failed\n");
                continue;
            }else if(pid == 0){
                close(listen_sock);
                if(fork()>0){//fork()两次,使得孙子进程变成孤儿进程受init回收,父进程直接回收子进程,解决父进程阻塞问题
                	exit(2);
                }
                else{
                while(1)
                {   
                fflush(stdout);
                char buf[1024];
                ssize_t i = read(new_sock, buf, sizeof(buf));
                if(i>0){
                printf("[%s][%d]:client say#%s\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buf);
                }else if(i == 0){
                    close(new_sock);
                    printf("[%s][%d]:client goodbye\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
                    break;         
                }else{
                    perror("read");
                    break;
                }
                printf("Enter to [%s][%d]#",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
                fflush(stdout);
                fgets(buf, sizeof(buf), stdin);
                buf[strlen(buf)-1] = '\0';
                write(new_sock, buf, strlen(buf));
                }
                }
            }else{
                close(new_sock);
                waitpid(pid,NULL,0);
            }
        }
        close(listen_sock);
        return 0;
    }
    


    client.c
    #include <sys/socket.h>
    #include<string.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include<stdio.h>
    #include<sys/types.h>
    
    
    void use(char *argv)
    {
        printf("#%s [ip_server] [port_srvera]\n", argv);
    }
    int main(int argc, char *argv[])
    {
        printf("main start\n");
        if(argc<3)
        {
            use(argv[0]);
            return 3;
        }
        printf("use is ok\n");
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if(sock<0){
           perror("socket");
           return 1;
        }      
        printf("create socket is ok\n");
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port =htons(atoi( argv[2]));
        server.sin_addr.s_addr = inet_addr((argv[1]));
        int conn = connect(sock, (struct sockaddr*)&server, sizeof(server));  
        if(conn<0){
            perror("connect");
            close(sock);
            return 2;
        }
        while(1)
        {
            printf("please enter#");
            fflush(stdout);
            char buf[1024];
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf)-1] = '\0';
            write(sock, buf, sizeof(buf));
            char* str = "quit";
            if(strcmp(buf, str)==0){
                 break;
            }
            printf("server echo#");
            fflush(stdout);
            ssize_t r2 = read(sock, buf, sizeof(buf));
            if(r2>0) {
                printf("%s\n",buf);
            }else{
                
                continue;
            }
        }
        close(sock);
        printf("client goodbye!!!\n");
        return 0;
    }
    


    (四)存在问题:
    但是在运行的时候发现一个问题,server启动后,然后启动client建立连接后,然后直接ctrl+c终止掉server,无法立即重启,必须等待半分钟才能重新启动。如下图

    这是因为在四次挥手断开连接时,主动断开的一方会进入TIME_WAIT状态,这是server还没有完全断开连接,还占着8080号端口,所以再次启动时创建监听套接字就无法在绑定上8080号端口。
    但是这是不合理的,因为在实际生活中,服务器一旦挂了,不能立即重启,可能会影响许多客户的体验,会造成很大的损失,那么如何解决。
    解决:

    解决这个问题的方法是使用setsockopt()设置socket描述符的,设置选项SO_REUSEADDR为1,表示允许创建端口号相同但IP地址不同的多个socket描述符。在server代码的socket()和bind()之间插入。
    展开全文
  • 基于BIO的TCP通信原理

    2019-07-09 11:15:04
    本文涉及的内容: ①TCP 客户端、服务器 的...一、TCP通信概述 客户端代码实现: package com.itheima.demo01.TCP; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; imp...
  • 文章目录前言一、TCP通信概述二、TCP的特点三、TCP通信的实现原理四、TCP通信原理实现(代码演示)总结 前言 今天继续这几天对于Java网络编程的一个小尾巴——TCP通信原理 一、TCP通信概述 TCP协议用来控制两个网络...
  • 【转】 C#版清晰易懂TCP通信原理解析(附demo) (点击上方蓝字,可快速关注我们) 来源:周见智 cnblogs.com/xiaozhi_5638/p/4244797.html 对.NET中网络编程写得比较多,主要原因有两个,一是我...
  • TCP通信原理-Socket套接字编程实现一个简单的客服聊天功能TCP和UDPSocket含义基于TCP的Socket客服聊天室实例源码及其注释代码关键现象分析Socket通信模型socket和IO阻塞TCP滑动窗口预防场景作用实现原理 TCP和UDP 在...
  • TCP与UDP通信的特点 TCP中的沾包现象 自定义应用层协议 TCPLibrary通信库介绍 Demo演示 未完成功能 源码下载 说明 我前面博客中有多篇文章讲到了.NET中的网络编程,与TCP和UDP相关的有: 1....
  • TCP与UDP通信的特点 TCP中的沾包现象 自定义应用层协议 TCPLibrary通信库介绍 Demo演示 未完成功能 源码下载 说明 我前面博客中有多篇文章讲到了.NET中的网络编程,与TCP和UDP相关的有: 1....
  • TCP大致工作原理介绍:   工作原理 TCP-IP详解卷1第17章中17.2节对TCP服务原理作了一个简明介绍(以下蓝色字体摘自《TCP-IP详解卷1第17章17.2节》): 尽管T C P和U D P都使用相同的网络层(I P),T C P却向应用层...
  • 系列文章目录 文章目录系列文章目录一、前言一、需求二、使用步骤客户端服务端三、完整代码总结 一、前言 我们平时玩QQ或者微信的群聊,是怎么实现的呢? 是你发一个消息直接全部给每个人发一遍吗?...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,047
精华内容 2,018
关键字:

tcp通信原理