socket网络编程:UDP通信、TCP通信

本篇文章默认你对UDP协议和TCP协议已经理解掌握了:UDP协议详解TCP协议详解

UDP网络通信编程

首先我们需要知道 UDP协议是传输层协议,面向数据报,无连接,不可靠传输,实时性高。
在这里插入图片描述

创建套接字:使进程与网卡之间建立联系

在这里插入图片描述
domain:地址域–>使用AF_INET(IPV4)
type:套接字类型
        SOCK_STREAM–流式套接字–默认对应的协议:TCP
        SOCK_DGRAM–数据报套接字–默认对应的协议:UDP
protocol:协议类型
        套接字类型默认协议–0
        IPPROTO_TCP–6
        IPPROTO_UDP–17
返回值:文件描述符–套接字操作句柄
在这里插入图片描述

为套接字绑定地址信息(ip/port)

在这里插入图片描述
sockfd:套接字操作句柄,socket函数的返回值
addr:地址类型(IPV4或者IPV6等等)
addrlen:地址信息长度
返回值:成功返回0,失败返回-1
在这里插入图片描述

客户端首先向服务端发送数据

服务端通常必须固定一个地址信息,不能随意改变;但是客户端的地址可以随意,因为数据发送给服务端,服务端就能获知客户端的地址。
在这里插入图片描述
sockfd:套接字操作句柄,socket函数的返回值
buf:要发送什么数据
len:数据的长度
flags:通常置0,阻塞操作(即缓冲区放满后等待)
dest_addr:接收端地址信息(ip+port)
addrlen:接收端地址信息长度
返回值:返回实际发送的数据长度,失败:-1
在这里插入图片描述

服务端接收数据

客户端发送的数据到达服务端主机后,服务端操作系统根据这个数据的地址信息,决定将这个数据放到哪一个套接字的缓冲区中(数据由指定进程处理);服务端通过创建套接字返回的描述符,在内核中找到套接字结构体,进而从缓冲区中取出数据。
在这里插入图片描述
sockfd:套接字操作句柄,socket函数的返回值
buf:从接收缓冲区拿到的数据存在在哪里
len:接收buf定义的最大长度,预留\0的位置
flags:通常置0,阻塞操作(即缓冲区中没有数据时等待)
src_addr:发送端地址信息(ip+port)
addrlen:发送端地址信息长度
返回值:返回实际接收的数据长度,失败:-1
在这里插入图片描述

关闭套接字–>释放内核中套接字占据的资源

在这里插入图片描述
fd:套接字操作句柄,socket函数的返回值
在这里插入图片描述

模拟实现UDP网络通信

#pragma once 
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string>

#define PORT 17777

class UdpSvr
{
    public:
        UdpSvr()
        {
            sock_ = -1;
        }

        ~UdpSvr()
        {}

        bool Create()
        {
            sock_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            if(sock_ < 0)
            {
                std::cout << "Create failed" << std::endl;
                return false;
            }
            return true;
        }

        bool Bind()
        {
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(PORT);
            addr.sin_addr.s_addr = inet_addr("0.0.0.0");
            int ret = bind(sock_, (struct sockaddr*)&addr, sizeof(addr));
            if(ret < 0)
            {
                std::cout << "Bind failed" << std::endl;
                return false;
            }
            return true;
        }

        bool Send(std::string& msg, struct sockaddr_in* peeraddr)
        {

            ssize_t send_size = sendto(sock_, msg.c_str(), msg.size(), 0, (struct sockaddr*)peeraddr, sizeof(struct sockaddr_in));
            if(send_size < 0)
            {
                std::cout << "Send failed" << std::endl;
                return false;
            }
            return true;
        }

        bool Recv(std::string& msg, struct sockaddr_in* peeraddr)
        {
            char buf[1024] = {0};
            socklen_t peeraddr_len = sizeof(struct sockaddr_in);
            ssize_t recv_size = recvfrom(sock_, &buf, sizeof(buf) - 1, 0, (struct sockaddr*)peeraddr, &peeraddr_len);
            if(recv_size < 0)
            {
                std::cout << "Recv failed" << std::endl;
                return false;
            }
            msg.assign(buf, recv_size);
            return true;
        }

        void Close()
        {
            close(sock_);
            sock_ = -1;
        }
    private:
        int sock_;
};
#include "udpsvr.hpp"

int main()
{
    UdpSvr us;
    if(!us.Create())
    {
        return 0;
    }

    if(!us.Bind())
    {
        return 0;
    }

    while(1)
    {
        std::string msg;
        struct sockaddr_in cliaddr;
        if(!us.Recv(msg, &cliaddr))
        {
            continue;
        }
        std::cout << "client say:" << msg << std::endl;

        std::cout << "server say:";
        fflush(stdout);
        std::cin >> msg;
        if(!us.Send(msg, &cliaddr))
        {
            continue;
        }
    }
    us.Close();
    return 0;
}
#include "udpsvr.hpp"

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        std::cout << "./cli [ip]" << std::endl;
        return 0;
    }
    std::string ip = argv[1];

    UdpSvr us;
    if(!us.Create())
    {
        return 0;
    }

    struct sockaddr_in svraddr;
    svraddr.sin_family = AF_INET;
    svraddr.sin_port = htons(PORT);
    svraddr.sin_addr.s_addr = inet_addr(ip.c_str());

    while(1)
    {
        std::string msg;
        std::cout << "client say:";
        fflush(stdout);
        std::cin >> msg;
        if(!us.Send(msg, &svraddr))
        {
            continue;
        }

        if(!us.Recv(msg, &svraddr))
        {
            continue;
        }
        std::cout << "server say:" << msg << std::endl;
    }
    us.Close();
    return 0;
}

TCP网络通信编程

首先我们需要知道 TCP协议是传输层协议,面向字节流,面向连接,可靠传输,安全性高。
在这里插入图片描述

创建套接字:使进程与网卡之间建立联系

在这里插入图片描述
domain:地址域–>使用AF_INET(IPV4)
type:套接字类型
        SOCK_STREAM–流式套接字–默认对应的协议:TCP
        SOCK_DGRAM–数据报套接字–默认对应的协议:UDP
protocol:协议类型
        套接字类型默认协议–0
        IPPROTO_TCP–6
        IPPROTO_UDP–17
返回值:文件描述符–套接字操作句柄
在这里插入图片描述

为套接字绑定地址信息(ip/port)

在这里插入图片描述
sockfd:套接字操作句柄,socket函数的返回值
addr:地址类型(IPV4或者IPV6等等)
addrlen:地址信息长度
返回值:成功返回0,失败返回-1
在这里插入图片描述

客户端流程
1、创建套接字:使进程与网卡之间建立联系
2、为套接字绑定地址信息(ip/port)
3、向服务端发起连接请求

在这里插入图片描述
sockfd:套接字描述符
addr:服务端的地址信息(当前客户端需要连接哪一个服务端的地址信息)
addrlen:地址信息长度
在这里插入图片描述

4、发送数据

在这里插入图片描述
sockfd:accept函数返回的操作系统内核新创建的socket操作句柄
buf:要给对端发送什么数据
len:发送数据的长度
flags:默认置 0–>阻塞发送
在这里插入图片描述

5、接收数据

在这里插入图片描述
sockfd:accept函数返回的操作系统内核新创建的socket操作句柄
buf:从接收缓冲区接收的数据放到哪里去
len:最大能放多少
flags:
        0:阻塞发送
        MSG_PEEK:探测接收,即不会将接收缓冲区当中的数据移除,而是拷贝接收缓冲区的数据(接收缓冲区当中原有的数据依然存在)
在这里插入图片描述

6、关闭套接字–>释放内核中套接字占据的资源

在这里插入图片描述
fd:套接字操作句柄,socket函数的返回值
在这里插入图片描述

服务端流程
1、创建套接字:使进程与网卡之间建立联系
2、为套接字绑定地址信息(ip/port)
3、监听

在这里插入图片描述
sockfd:监听套接字的操作句柄
backlog:已完成连接队列的大小
        同一时刻,服务端最大的并发连接数
        监听的时候,一旦有新的连接到来,OS会对新的连接分配一个socket,进行一对一服务。
在这里插入图片描述
在这里插入图片描述

4、获取连接

在这里插入图片描述
sockfd:监听套接字的操作句柄
addr:客户端地址信息
addrlen:客户端地址信息的长度,输入输出型参数
返回值:返回的是操作系统内核创建的新的socket的文件描述符
在这里插入图片描述

5、接收数据
6、发送数据
7、关闭套接字–>释放内核中套接字占据的资源

模拟实现TCP网络通信

Tcp通信时必须使用多进程或者多线程:因为accept会阻塞

对于单个执行流而言,由于代码是串行运行的

  1. 将accept函数放到while(1)循环当中
    每次循环要往下继续执行,则必须有一个新的连接到来,并且在往下执行的时候,是和新的客户端沟通
  2. 将accept函数放到while(1)循环外面
    则整个服务器只能和一个客户端进行业务的收发数据,但是并不能代表不能和服务器建立连接,以为TCP的三次握手是在listen阶段,告诉操作系统可以监听,连接的建立右操作系统完成,完成后的连接放到已完成连接队列当中去。
多进程
#pragma once 
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>

#define PORT 17778

class TcpSvr
{
    public:
        TcpSvr()
        {
            sock_ = -1;
        }

        ~TcpSvr()
        {}

        bool CreateSock()
        {
            sock_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if(sock_ < 0)
            {
                std::cout << "Create failed" << std::endl;
                return false;
            }
            return true;
        }

        bool Bind()
        {
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(PORT);
            addr.sin_addr.s_addr = inet_addr("0.0.0.0");
            int ret = bind(sock_, (struct sockaddr*)&addr, sizeof(addr));
            if(ret < 0)
            {
                std::cout << "Bind failed" << std::endl;
                return false;
            }
            return true;
        }

        bool Listen(int backlog = 5)
        {
            int ret = listen(sock_, backlog);
            if(ret < 0)
            {
                std::cout << "Listen failed" << std::endl;
                return false;
            }
            return true;
        }

        bool Accept(TcpSvr& ts, struct sockaddr_in* addr = NULL)
        {
            struct sockaddr_in cliaddr;
            socklen_t cliaddr_len = sizeof(cliaddr);
            int newsock = accept(sock_, (struct sockaddr*)&cliaddr, &cliaddr_len);
            if(newsock < 0)
            {
                std::cout << "Accept failed" << std::endl;
                return false;
            }
            ts.sock_ = newsock;

            if(addr != NULL)
            {
                memcpy(addr, &cliaddr, cliaddr_len);
            }
            return true;
        }

        bool Connect(struct sockaddr_in* svraddr)
        {
            int ret = connect(sock_, (struct sockaddr*)svraddr, sizeof(struct sockaddr_in));
            if(ret < 0)
            {
                std::cout << "Connect failed" << std::endl;
                return false;
            }
            return true;
        }

        bool Send(std::string& msg)
        {
           ssize_t send_size = send(sock_, msg.c_str(), msg.size(), 0);
           if(send_size < 0)
           {
                std::cout << "Send failed" << std::endl;
                return false;
           }
           return true;
        }

        bool Recv(std::string& msg)
        {
            char buf[1024] = {0};
            ssize_t recv_size = recv(sock_, buf, sizeof(buf) - 1, 0);
            if(recv_size < 0)
            {
                std::cout << "Recv failed" << std::endl;
                return false;
            }
            else if(recv_size == 0)
            {
                std::cout << "Peer close connect" << std::endl;
                return false;
            }
            msg.assign(buf, recv_size);
            return true;
        }

        void Close()
        {
            close(sock_);
            sock_ = -1;
        }
    private:
        int sock_;
};
#include "tcpsvr.hpp"
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>

void sigcb(int signo)
{
    //等待子进程
    (void)signo;
    while(1)
    {
        waitpid(-1, NULL, WNOHANG);
    }
}

int main()
{
    signal(SIGCHLD, sigcb); 

    TcpSvr ts;
    if(!ts.CreateSock())
    {
        return 0;
    }

    if(!ts.Bind())
    {
        return 0;
    }

    if(!ts.Listen())
    {
        return 0;
    }


    while(1)
    {
        TcpSvr ts2;
        struct sockaddr_in cliaddr;
        if(!ts.Accept(ts2, &cliaddr))
        {
            continue;
        }
        std::cout << "Server have a new connect, " << inet_ntoa(cliaddr.sin_addr) << ":" << ntohs(cliaddr.sin_port) << std::endl; 
        
        int pid = fork();
        if(pid < 0)
        {
            std::cout << "Create fork failed" << std::endl;
            exit(1);
        }
        else if(pid == 0)
        {
            //child
            while(1)
            {
                std::string msg;
                if(!ts2.Recv(msg))
                {
                    break;
                }
                std::cout << "cilent say:" << msg << std::endl;

                std::cout << "server say:";
                fflush(stdout);
                std::cin >> msg;
                ts2.Send(msg);
            }
            ts2.Close();
            exit(1);
        }
        else 
        {
            //father
            ts2.Close();
        }
    }

    ts.Close();
    return 0;
}
#include "tcpsvr.hpp"

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        std::cout << "./cli [ip]" << std::endl;
        return 0;
    }
    std::string ip = argv[1];

    TcpSvr ts;
    if(!ts.CreateSock())
    {
        return 0;
    }

    struct sockaddr_in svraddr;
    svraddr.sin_family = AF_INET;
    svraddr.sin_port = htons(PORT);
    svraddr.sin_addr.s_addr = inet_addr(ip.c_str());
    if(!ts.Connect(&svraddr))
    {
        return 0;
    }

    while(1)
    {
        std::string msg;
        std::cout << "client say:";
        fflush(stdout);
        std::cin >> msg;
        ts.Send(msg);

        ts.Recv(msg);
        std::cout << "server say:" << msg << std::endl;
    }

    ts.Close();
    return 0;
}
多线程
#pragma once 
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>

#define PORT 17778

class TcpSvr
{
    public:
        TcpSvr()
        {
            sock_ = -1;
        }

        ~TcpSvr()
        {}

        bool CreateSock()
        {
            sock_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if(sock_ < 0)
            {
                std::cout << "Create failed" << std::endl;
                return false;
            }
            return true;
        }

        bool Bind()
        {
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(PORT);
            addr.sin_addr.s_addr = inet_addr("0.0.0.0");
            int ret = bind(sock_, (struct sockaddr*)&addr, sizeof(addr));
            if(ret < 0)
            {
                std::cout << "Bind failed" << std::endl;
                return false;
            }
            return true;
        }

        bool Listen(int backlog = 5)
        {
            int ret = listen(sock_, backlog);
            if(ret < 0)
            {
                std::cout << "Listen failed" << std::endl;
                return false;
            }
            return true;
        }

        bool Accept(TcpSvr* ts, struct sockaddr_in* addr = NULL)
        {
            struct sockaddr_in cliaddr;
            socklen_t cliaddr_len = sizeof(cliaddr);
            int newsock = accept(sock_, (struct sockaddr*)&cliaddr, &cliaddr_len);
            if(newsock < 0)
            {
                std::cout << "Accept failed" << std::endl;
                return false;
            }
            ts->sock_ = newsock;

            if(addr != NULL)
            {
                memcpy(addr, &cliaddr, cliaddr_len);
            }
            return true;
        }

        bool Connect(struct sockaddr_in* svraddr)
        {
            int ret = connect(sock_, (struct sockaddr*)svraddr, sizeof(struct sockaddr_in));
            if(ret < 0)
            {
                std::cout << "Connect failed" << std::endl;
                return false;
            }
            return true;
        }

        bool Send(std::string& msg)
        {
           ssize_t send_size = send(sock_, msg.c_str(), msg.size(), 0);
           if(send_size < 0)
           {
                std::cout << "Send failed" << std::endl;
                return false;
           }
           return true;
        }

        bool Recv(std::string& msg)
        {
            char buf[1024] = {0};
            ssize_t recv_size = recv(sock_, buf, sizeof(buf) - 1, 0);
            if(recv_size < 0)
            {
                std::cout << "Recv failed" << std::endl;
                return false;
            }
            else if(recv_size == 0)
            {
                std::cout << "Peer close connect" << std::endl;
                return false;
            }
            msg.assign(buf, recv_size);
            return true;
        }

        void Close()
        {
            close(sock_);
            sock_ = -1;
        }
    private:
        int sock_;
};
#include "tcpsvr.hpp"

void* ThreadStart(void* arg)
{
    pthread_detach(pthread_self());
    TcpSvr* ts = (TcpSvr*)arg;
    while(1)
    {
        std::string msg;
        ts->Recv(msg);
        std::cout << "client say:" << msg << std::endl;

        std::cout << "server say:";
        fflush(stdout);
        std::cin >> msg;
        ts->Send(msg);
    }
    ts->Close();
    delete ts;
}

int main()
{
    TcpSvr ts;
    if(!ts.CreateSock())
    {
        return 0;
    }

    if(!ts.Bind())
    {
        return 0;
    }

    if(!ts.Listen())
    {
        return 0;
    }

    while(1)
    {
        TcpSvr* ts2 = new TcpSvr();
        struct sockaddr_in cliaddr;
        if(!ts.Accept(ts2, &cliaddr))
        {
            continue;
        }
        std::cout << "Server have a new conncet, " << inet_ntoa(cliaddr.sin_addr) << ":" << ntohs(cliaddr.sin_port) << std::endl;
        
        pthread_t tid;
        int ret = pthread_create(&tid, NULL, ThreadStart, (void*)ts2);
        if(ret < 0)
        {
            std::cout << "Create thread failed" << std::endl;
            return 0;
        }
    }
    
    ts.Close();
    return 0;
}
#include "tcpsvr.hpp"

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        std::cout << "./cli [ip]" << std::endl;
        return 0;
    }
    std::string ip = argv[1];

    TcpSvr ts;
    if(!ts.CreateSock())
    {
        return 0;
    }

    struct sockaddr_in svraddr;
    svraddr.sin_family = AF_INET;
    svraddr.sin_port = htons(PORT);
    svraddr.sin_addr.s_addr = inet_addr(ip.c_str());
    if(!ts.Connect(&svraddr))
    {
        return 0;
    }

    while(1)
    {
        std::string msg;
        std::cout << "client say:";
        fflush(stdout);
        std::cin >> msg;
        ts.Send(msg);

        ts.Recv(msg);
        std::cout << "server say:" << msg << std::endl;
    }

    ts.Close();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值