网络通信-TCP协议

        TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议

一、tcp协议头部项解释

        TCP 头部包含了用于管理两个端点之间通信的控制信息。

  1. 源端口号(Source Port

    • 16位,标识发送方的端口号。
  2. 目的端口号(Destination Port)

    • 16位,标识接收方的端口号。
  3. 序列号(Sequence Number)

    • 32位,用于为字节流中的每个字节进行编号,确保数据的有序传输。
  4. 确认号(Acknowledgment Number)

    • 32位,期望收到的下一个字节的序列号,用作对已接收数据的确认。
  5. 数据偏移(Data Offset)

    • 4位,指示 TCP 头部的长度(以32位字为单位),因为 TCP 头部可能包含可变数量的选项字段。
  6. 保留(Reserved)

    • 6位,保留供将来使用,当前必须设置为0。
  7. 控制位(Control Bits)

    • 6位,用于控制TCP的不同功能,包括:
      • URG(紧急指针有效)
      • ACK(确认号有效)
      • PSH(接收方应尽快将缓冲区数据推送给应用程序)
      • RST(重置连接)
      • SYN(同步序列开始,用于建立连接)
      • FIN(结束一个连接)
  8. 窗口大小(Window Size)

    • 16位,用于流量控制,指示接收端还能接收多少字节的数据。
  9. 校验和(Checksum)

    • 16位,用于检测头部和数据的完整性。
  10. 紧急指针(Urgent Pointer)

    • 16位,仅当URG控制位被设置时使用,指示紧急数据的结束位置。
  11. 选项(Options)

    • 可变长度,用于提供额外的信息或配置,如最大报文段长度(MSS)、窗口缩放因子、选择性确认(SACK)等。

二、tcp工作在那一层

        传输层

三、windows平台C++实现demo

        服务端代码

#include <winsock2.h>
#include <windows.h>
#include <iostream>

#pragma comment(lib, "ws2_32.lib")  // 指定链接Winsock库

#define PORT 8888  // 服务器监听的端口

int main() {
    WSADATA wsaData;
    SOCKET serverSocket, clientSocket;
    struct sockaddr_in serverAddr, clientAddr;
    int clientAddrLen = sizeof(clientAddr);
    char buffer[1024];

    // 初始化Winsock
    WSAStartup(MAKEWORD(2, 2), &wsaData);

    // 创建套接字
    serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (serverSocket == INVALID_SOCKET) {
        std::cerr << "Could not create socket: " << WSAGetLastError() << std::endl;
        return 1;
    }

    // 设置服务器地址信息
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(PORT);

    // 绑定套接字
    if (bind(serverSocket, (PSOCKADDR)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        std::cerr << "Bind failed with error: " << WSAGetLastError() << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return 1;
    }

    // 开始监听
    listen(serverSocket, 5);

    // 接受客户端连接
    clientSocket = accept(serverSocket, (PSOCKADDR)&clientAddr, &clientAddrLen);
    if (clientSocket == INVALID_SOCKET) {
        std::cerr << "Accept failed: " << WSAGetLastError() << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return 1;
    }

    // 接收数据
    int iResult = recv(clientSocket, buffer, sizeof(buffer), 0);
    if (iResult > 0) {
        std::cout << "Bytes received: " << iResult << std::endl;
        std::cout << "Message: " << buffer << std::endl;
    }

    // 发送响应数据
    const char* response = "Hello, client!";
    iResult = send(clientSocket, response, (int)strlen(response), 0);
    if (iResult == SOCKET_ERROR) {
        std::cerr << "Send failed: " << WSAGetLastError() << std::endl;
    }

    // 关闭套接字
    closesocket(clientSocket);
    closesocket(serverSocket);

    // 清理Winsock
    WSACleanup();
    return 0;
}

客户端代码

#include <winsock2.h>
#include <windows.h>
#include <iostream>

#pragma comment(lib, "ws2_32.lib")  // 指定链接Winsock库

#define IP "127.0.0.1"  // 服务器IP地址
#define PORT 8888        // 服务器端口

int main() {
    WSADATA wsaData;
    SOCKET clientSocket;
    struct sockaddr_in serverAddr;
    char buffer[1024];

    // 初始化Winsock
    WSAStartup(MAKEWORD(2, 2), &wsaData);

    // 创建套接字
    clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (clientSocket == INVALID_SOCKET) {
        std::cerr << "Could not create socket: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    // 设置服务器地址信息
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr(IP);
    serverAddr.sin_port = htons(PORT);

    // 连接到服务器
    if (connect(clientSocket, (PSOCKADDR)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        std::cerr << "Connect failed: " << WSAGetLastError() << std::endl;
        closesocket(clientSocket);
        WSACleanup();
        return 1;
    }

    // 发送数据
    const char* message = "Hello, server!";
    int iResult = send(clientSocket, message, (int)strlen(message), 0);
    if (iResult == SOCKET_ERROR) {
        std::cerr << "Send failed: " << WSAGetLastError() << std::endl;
    }

    // 接收响应数据
    iResult = recv(clientSocket, buffer, sizeof(buffer), 0);
    if (iResult > 0) {
        std::cout << "Bytes received: " << iResult << std::endl;
        std::cout << "Message: " << buffer << std::endl;
    }

    // 关闭套接字
    closesocket(clientSocket);

    // 清理Winsock
    WSACleanup();
    return 0;
}

上述代码,只是一个最简单的实力。未考虑多线程,多个客户端同时向服务端连接情况。

涉及的结构体和API

数据结构:
1. SOCKET:用于标识一个通信端点的句柄。
2. sockaddr_in:用于存储IPv4地址信息的结构体,包括地址族(sin_family)、IP地址(sin_addr)和端口号(sin_port)。
3. WSADATA:用于存储与Winsock DLL的版本信息相关的数据。

Winsock API:
1. WSAStartup:初始化Winsock服务,加载Winsock DLL,并设置应用程序的版本号。
2. socket:创建一个端点(即套接字)。
3. bind:将一个本地地址绑定到套接字上。
4. listen:使套接字成为监听套接字,等待进入连接。
5. accept:接受一个连接请求,返回一个新的套接字用于通信。
6. connect:在客户端上,用于连接到服务器的套接字。
7. send:发送数据到连接的套接字。
8. recv:从连接的套接字接收数据。
9. closesocket:关闭套接字,释放资源。
10. WSACleanup:清除Winsock使用的所有资源,与WSAStartup相对应。

其它:
1. inet_addr:将点分十进制的IP地址转换为网络字节顺序的整数。
2. htons:将主机字节顺序的16位整数转换为网络字节顺序。
3. htonl:将主机字节顺序的32位整数转换为网络字节顺序。

概念:
- 端口号(PORT):用于标识特定的服务或进程。
- IP地址:标识网络中的设备位置。
- 字节序转换:由于网络通信使用大端字节序,因此需要将主机字节序转换为网络字节序。

四、连接的建立过程-三次握手

        第一次:客户端:向服务端发送SYN包

        第二次:服务端:向客户端SYN、ACK包

        第三次:客户端:向服务端ACK包

注:在建立连接时,第三次握手是可以携带数据的。

五、连接的断开过程-四次挥手

如果是客户端想要断开连接:

        客户端:发送FIN包

        服务端:发送ACK包

        服务端:发送FIN包

        客户端:发送ACK包

如果是服务端想要断开连接

        服务端:发送FIN包

        客户端:发送ACK包

        客户端:发送FIN包

        服务端:发送ACK包

六、SYN攻击

        当服务器处于listen监听状态时,所有的客户端都可以向他发送连接请求。

       此时,如果有人恶意连接,即持续不断的发送大量的SYN包给服务器,此时服务器会忙不过来。待连接队列会满。正常需要连接的普通客户端的SYN包可能无法被处理,称之为SYN攻击

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值