重温TCP/UDP基础–1(UDP&TCP)

  • Post author:
  • Post category:其他



TCP/UDP 基本概念

IP:每一天计算机在某一网络中的唯一身份标识

PORT:一个端口号对应一个应用程序,当做应用程序的标识


网络编程:Socket

Socket建立计算机与计算机之间的链接,建立链接之后能够发送数据包

客户端:谁发请求谁就是客户端

服务端:谁接收请求谁就是服务端


网络模型:四层

第一层:应用层:HTTP客户端/服务端

第二层:传输层:协议(TCP/UDP)

第三层:网络层:IP

第四层:链路层:以太网协议

TCP:面向链接,客户端于服务器建立链接时要经历三次握手,采用字节进行数据的传输,较为安全,但是效率比UDP慢

SYN:建立链接的标志,由客户端发送,一般都是SYN=j

ACK;确认报文,ACK首次 = 1将SYN+1发送给客户端,客户端用来确认请求被正确响应


三次握手:

第一次:客户端发起链接请求,服务端接收客户端的链接请求(验证客户端发送以及服务端接收功能是否正常)

第二次:服务端发送响应,客户端接收(验证服务端的发送和客户端的接收是否正常)

第三次:客户端发送数据给服务端,服务端接收数据(验证客户端的数据发送和服务端的数据接收是否正常)


四次挥手:

第一次:客户端发送FIN标志给服务端

第二次:服务端响应客户端的FIN请求,答复ACK,ACK是在客户端的SYN基础上+1

第三次:服务端断开链接

第四次:客户端响应服务器发送的ACK

UDP:面向无连接、存在丢包、每一个数据包的大小限制在64K以内,不安全、不靠谱的链接,

HTTP:协议的底层就是采用TCP协议


UDP服务器四部曲:

初始化socket环境:WSAStartup

构造socket对象:socket()

绑定需要占用的IP以及port:bing()

接收和发送信息:recvfrom()、sendto()

代码:


UDPServer.hpp

#pragma once
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
using namespace std;
// Adding a dynamic library
#pragma comment(lib, "ws2_32.lib")
class UDPServer {
public:
    UDPServer();
    ~UDPServer();
    // 初始化socket环境
    bool InitSocketEnv();
    // 构造socket对象
    bool ConstrunctSocketObj();
    // 绑定需要占用的IP以及port
    bool BindIpAndPort();
    // 接收和发送信息
    void RecvAndSendMsg();
private:
    SOCKET m_Socket;
    SOCKADDR_IN m_BindAddress; // Bind Address
    SOCKADDR_IN m_RemoteAddress; // Bind remote Address
    int m_RemoteAddressLen;
};


UDPServer.cpp:

#include "UDPServer.h"
const int BUF_LEN = 1024;
UDPServer::UDPServer() : m_Socket(-1), m_RemoteAddressLen(0)
{
    memset(static_cast<void*>(&m_BindAddress), 0x00, sizeof(SOCKADDR_IN));
    memset(static_cast<void*>(&m_RemoteAddress), 0x00, sizeof(SOCKADDR_IN));
}
UDPServer::~UDPServer()
{
    closesocket(m_Socket);
    WSACleanup();
}
// 初始化socket环境
bool UDPServer::InitSocketEnv()
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        cout << "UDPServer::InitSocketEnv(): WSAStartup error: " << GetLastError()  << endl;
        return false;
    }
    return true;
}
// 构造socket对象
bool UDPServer::ConstrunctSocketObj()
{
    m_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (m_Socket == INVALID_SOCKET) {
        cout << "UDPServer::ConstrunctSocketObj() new socket failed: " <<  GetLastError() << endl;
        closesocket(m_Socket);
        m_Socket = INVALID_SOCKET;
        return false;
    }
    cout << "UDPServer::ConstrunctSocketObj() new socket successful: socket id [ "  << m_Socket << " ]" << endl;
    return true;
}
// 绑定需要占用的IP以及port
bool UDPServer::BindIpAndPort()
{
    const char* pIpStr = "127.0.0.1";
    int nPort = 8080;
    m_BindAddress.sin_family = AF_INET;
    m_BindAddress.sin_addr.S_un.S_addr = inet_addr(pIpStr);
    m_BindAddress.sin_port = htons(nPort);
    auto binRet = bind(m_Socket, (sockaddr*)&m_BindAddress, sizeof(SOCKADDR));
    if (binRet == SOCKET_ERROR) {
        pIpStr = nullptr;
        cout << "UDPServer::BindIpAndPort() when bind Address failed: " <<  GetLastError() << endl;
        return false;
    }
    pIpStr = nullptr;
    return true;
}
// 接收和发送信息
void UDPServer::RecvAndSendMsg()
{
    char recvBuffer[BUF_LEN] = { 0 };
    char sendBuffer[BUF_LEN] = "Nice to meet you, too!";
    m_RemoteAddressLen = sizeof(m_RemoteAddress);
    std::printf("已设置绑定占用的链接,其中IP:%s, port: %d\n",  inet_ntoa(m_BindAddress.sin_addr), ntohs(m_BindAddress.sin_port));
    while (true) {
        int recvLen = recvfrom(m_Socket, recvBuffer, BUF_LEN, 0,  (sockaddr*)&m_RemoteAddress, &m_RemoteAddressLen);
        if (recvLen > 0) {
            std::printf("接收到一个链接, 其中IP: %s, port: %d\n",  inet_ntoa(m_RemoteAddress.sin_addr), m_RemoteAddress.sin_port);
            cout << "消息内容: " << recvBuffer << endl;
        }
        int SendLen = sendto(m_Socket, sendBuffer, BUF_LEN, 0,  (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
        if (SendLen > 0) {
            cout << "发送到客户端的信息:" << sendBuffer << endl;
        }
    }
}


UDP客户端四部曲:

初始化socket环境:WSAStartup

构造socket对象:socket()

绑定需要占用的IP以及port:bing()

接收和发送信息:sendto(),recvfrom()、


UDPClinet.hpp

#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
// Adding a dynamic library
#pragma comment(lib, "ws2_32.lib")
class UDPClient {
public:
    UDPClient();
    ~UDPClient();
    // 初始化socket环境
    bool InitSocketEnv();
    // 构造socket对象
    bool ConstrunctSocketObj();
    // 绑定需要占用的IP以及port
    void BindIpAndPort();
    // 发送和接收信息
    void SendAndRecvMsg();
private:
    SOCKET m_Socket;
    SOCKADDR_IN m_RemoteAddress;
    int m_RemoteAddressLen;
};


UDPClient.cpp

#include "UDPClient.h"
using namespace std;
const int BUF_LEN = 1024;
UDPClient::UDPClient() : m_Socket(-1), m_RemoteAddressLen(0)
{
    memset(static_cast<void*>(&m_RemoteAddress), 0x00, sizeof(SOCKADDR_IN));
}
UDPClient::~UDPClient()
{
    closesocket(m_Socket);
    WSACleanup();
}
bool UDPClient::InitSocketEnv()
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        cout << "UDPClient::InitSocketEnv(): WSAStartup error: " << GetLastError()  << endl;
        return false;
    }
    return true;
}
bool UDPClient::ConstrunctSocketObj()
{
    m_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (m_Socket == INVALID_SOCKET) {
        cout << "UDPClient::ConstrunctSocketObj(): new socket id failed: " <<  GetLastError() << endl;
        closesocket(m_Socket);
        m_Socket = INVALID_SOCKET;
        return false;
    }
    return true;
}
void UDPClient::BindIpAndPort()
{
    const char* pIpStr = "127.0.0.1";
    int nPort = 8080;
    m_RemoteAddress.sin_family = AF_INET;
    m_RemoteAddress.sin_port = htons(nPort);
    m_RemoteAddressLen = sizeof(m_RemoteAddress);
    inet_pton(AF_INET, pIpStr, &m_RemoteAddress.sin_addr);
}
void UDPClient::SendAndRecvMsg()
{
    char recvBuffer[BUF_LEN] = { 0 };
    char sendBuffer[BUF_LEN] = "Nice to meet you!";
    while (true) {
        int sendLen = sendto(m_Socket, sendBuffer, BUF_LEN, 0,  (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
        if (sendLen > 0) {
            std::printf("发送到服务器的链接,其中ip: %s, port: %d\n",  inet_ntoa(m_RemoteAddress.sin_addr), ntohs(m_RemoteAddress.sin_port));
            cout << "发送到服务器的信息:" << sendBuffer << endl;
        }
        int recvLen = recvfrom(m_Socket, recvBuffer, BUF_LEN, 0,  (sockaddr*)&m_RemoteAddress, &m_RemoteAddressLen);
        if (recvLen > 0) {
            std::printf("接收到来自服务器的消息,其中ip: %s, port: %d\n",  inet_ntoa(m_RemoteAddress.sin_addr), ntohs(m_RemoteAddress.sin_port));
            cout << "接收到服务器的消息: " << recvBuffer << endl;
        }
    }
}


main执行函数:

// TCP_Program.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <thread>
#include "UDPServer.h"
#include "UDPClient.h"
void RunUDPServer()
{
    // 先启动UDP的服务器
    UDPServer udpServer = UDPServer();
    udpServer.InitSocketEnv();
    udpServer.ConstrunctSocketObj();
    udpServer.BindIpAndPort();
    udpServer.RecvAndSendMsg();
}
void RunUDPClinet()
{
    // 启动UDP客户端
    UDPClient udpClient = UDPClient();
    udpClient.InitSocketEnv();
    udpClient.ConstrunctSocketObj();
    udpClient.BindIpAndPort();
    udpClient.SendAndRecvMsg();
}
int main()
{
    // 多线程执行
    std::thread thrd_1(RunUDPServer);
    std::thread thrd_2(RunUDPClinet);
    thrd_1.join();
    thrd_2.join();
    return 0;
}

——————————————————————————————————————


简易TCP服务器五部曲

初始化变量并加载win sock相关版本库

构造socket

绑定server端的ip以及端口信息

监听server端的socket

等待一个远程客户端连接并且返回一个socket

向这个成功建立连接的socket发送信息


code:


TCPServer.h

#pragma once
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
/*  提供了网络相关的API支持
    如使用WSAStartup、WSACleanup方法
    引入这个动态库的两种方法:
    第一种:像下面直接在代码中引入
    第二种:属性设置->连接器->输入->附加依赖项->编辑->输入"ws2_32.lib"
*/
#pragma comment(lib, "ws2_32.lib")
class TCPServer {
public:
    TCPServer();
    ~TCPServer();
    bool BuildSocket();
    int BindServerAddress();
    int ListenClient();
    SOCKET WaitAccept();
    int SendMsgToClient(SOCKET& in_SocketClinet);
private:
    SOCKET m_Socket;
    int m_RemoteAddressLen;
    sockaddr_in m_SockAddress;
    sockaddr_in m_RemoteAddress;
};


TCPServer.cpp

#include "TCPServer.h"
using namespace std;
TCPServer::TCPServer()
{
    m_Socket = INVALID_SOCKET;
    m_RemoteAddressLen = sizeof(sockaddr_in);
    memset(static_cast<void*>(&m_SockAddress), 0x00, sizeof(sockaddr_in));
    memset(static_cast<void*>(&m_RemoteAddress), 0x00, sizeof(sockaddr_in));
    WSADATA wsData;
    WSAStartup(MAKEWORD(2, 2), &wsData);
}
TCPServer::~TCPServer()
{
    closesocket(m_Socket);
    WSACleanup();
}
bool TCPServer::BuildSocket()
{
    m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_Socket == INVALID_SOCKET) {
        cout << "TCPServer::BuildSocket() build socket failed: " << GetLastError()  << endl;
        m_Socket = INVALID_SOCKET;
        return false;
    }
    cout << "TCPServer::BuildSocket() build socket success!" << endl;
    return true;
}
int TCPServer::BindServerAddress()
{
    m_SockAddress.sin_family = AF_INET;
    m_SockAddress.sin_addr.S_un.S_addr = INADDR_ANY; // 代表本地的地址都可以访问
    //m_SockAddress.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    m_SockAddress.sin_port = htons(8080);
    int nRet = bind(m_Socket, (sockaddr*)&m_SockAddress, sizeof(sockaddr_in));
    if (nRet == SOCKET_ERROR) {
        cout << "TCPServer::BindServerAddress() bind clinet ip&port failed: " <<  GetLastError() << endl;
        return nRet;
    }
    cout << "TCPServer::BindServerAddress() bind client ip&port success!" << endl;
    return nRet;
    
}
int TCPServer::ListenClient()
{
    // 第二个参数说明: 待处理连接队列的最大长度
    int nRet = listen(m_Socket, 15);
    if (nRet == SOCKET_ERROR) {
        cout << "TCPServer::ListenClient() listen client failed: " <<  GetLastError() << endl;
        return nRet;
    }
    cout << "TCPServer::ListenClient() listen client success!" << endl;
    return nRet;
}
SOCKET TCPServer::WaitAccept()
{
    SOCKET nRet = INVALID_SOCKET;
    while (true) {
        nRet = accept(m_Socket, (sockaddr*)&m_RemoteAddress, &m_RemoteAddressLen);
        if (nRet == INVALID_SOCKET) {
            cout << "TCPServer::WaitAccept() accpet a error socket: " <<  GetLastError() << endl;
            return nRet;
        }
        cout << "TCPServer::WaitAccept() a new client joined serve, IP: " <<  inet_ntoa(m_RemoteAddress.sin_addr) << endl;
        SendMsgToClient(nRet);
    }
    return nRet;
}
int TCPServer::SendMsgToClient(SOCKET& in_SocketClinet)
{
    char RecvBuffer[1024] = { "Hello! I'm TCP Server. i had recv you msg!" };
    int nRet = send(in_SocketClinet, RecvBuffer, 1024, 0);
    if (nRet == -1) {
        cout << "TCPServer::SendMsgToClient() send msg to clinet failed: " <<  GetLastError() << endl;
        return nRet;
    }
    cout << "TCPServer::SendMsgToClient() send msg to client success!" << endl;
    return nRet;
}

TCP客户端四部曲:
初始化win socket版本库信息

构建socket

连接服务器

连接成功后给服务器发送消息并接收来自服务器的响应


code:


TCPClient.hpp

#pragma once
#include <WS2tcpip.h>
#include <WinSock2.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")
class TCPClinet {
public:
    TCPClinet();
    ~TCPClinet();
    int BuildSocket();
    int ConnetServer();
    int RecvFromServer();
private:
    SOCKET m_Socket;
    int m_AddressLen;
    sockaddr_in m_Address;
};


TCPClinet.cpp

#include "TCPClient.h"
using namespace std;
TCPClinet::TCPClinet()
{
    m_Socket = INVALID_SOCKET;
    m_AddressLen = sizeof(sockaddr_in);
    memset(static_cast<void*>(&m_Address), 0x00, sizeof(sockaddr_in));
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
}
TCPClinet::~TCPClinet()
{
    closesocket(m_Socket);
    WSACleanup();
}
int TCPClinet::BuildSocket()
{
    m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_Socket == INVALID_SOCKET) {
        cout << "TCPClinet::BuildSocket() build sokcet failed: " << GetLastError()  << endl;
        return INVALID_SOCKET;
    }
    cout << "TCPClinet::BuildSocket() build socket success!" << endl;
    return 0;
}
int TCPClinet::ConnetServer()
{
    m_Address.sin_family = AF_INET;
    m_Address.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    m_Address.sin_port = htons(8080);
    int nRet = connect(m_Socket, (sockaddr*)&m_Address, m_AddressLen);
    if (nRet == SOCKET_ERROR) {
        cout << "TCPClinet::ConnetServer() connect server failed: " <<  GetLastError() << endl;
        return nRet;
    }
    cout << "TCPClinet::ConnetServer() ocnnect Server success!" << endl;
    return nRet;
}
int TCPClinet::RecvFromServer()
{
    char RecvBuffer[1024] = { 0 };
    int nRet = recv(m_Socket, RecvBuffer, 1024, 0);
    if (nRet == -1) {
        cout << "TCPClinet::RecvFromServer() recv msg from server failed: " <<  GetLastError() << endl;
        return nRet;
    }
    cout << "TCPClinet::RecvFromServer() recv a msg from server: " << RecvBuffer  << endl;
    return nRet;
}

main.cpp

// TCP_Program.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <thread>
#include "UDPServer.h"
#include "UDPClient.h"
#include "TCPServer.h"
#include "TCPClient.h"

using namespace std;

void RunTCPServer()
{
    TCPServer tcpServer = TCPServer();
    tcpServer.BuildSocket();
    tcpServer.BindServerAddress();
    tcpServer.ListenClient();
    tcpServer.WaitAccept();
}

void RunTCPClinet()
{
    TCPClinet tcpClient = TCPClinet();
    tcpClient.BuildSocket();
    tcpClient.ConnetServer();
    tcpClient.RecvFromServer();
}

int main()
{
    std::thread thrd_1(RunTCPServer);
    Sleep(3000);
    cout << "--------------------------------------------" << endl;
    std::thread thrd_2(RunTCPClinet);
    cout << "--------------------------------------------" << endl;
    thrd_1.join();
    Sleep(1000);
    thrd_2.join();
    
    return 0;
}


相关帖子



面试必备!TCP协议经典十五连问!_Hello,C++!的博客-CSDN博客_tcp协议面试题