【计算机网络】网络编程套接字(一)

news2024/11/26 15:37:37

目录

1.预备知识

1.1.理解源IP地址和目的IP地址

1.2.认识端口号

1.2.1.理解"端口号"和"进程ID"

1.2.2.理解源端口号和目的端口号

1.3.认识TCP/UDP协议

1.3.1.TCP协议

1.3.2.UDP协议

1.4.网络字节序

网络字节序和主机字节序的转换

2.socket编程接口

2.1.sockaddr结构

struct sockaddr_in 的具体结构:

2.2.socket常见API

2.3.点分十进制到init32_t之间的转化的原理

2.3.1. 12345   --->  “27.48.0.0”

2.3.2. “27.48.0.0”   --->  12345 

2.4.查看网络情况

2.4.1.netstat命令

2.5.udp协议实现网络字典

​编辑

2.6.远端的shell解释器

2.7.udp——实现网络聊天室

2.8.Windows版本的的网络套接字




1.预备知识

1.1.理解源IP地址和目的IP地址

在IP数据包头部中,有两个IP地址,分别叫做源IP地址,和目的IP地址。确定的在路由期间的方向性。为转发的每一个阶段提供方向。

1.2.认识端口号

思考:我们光有IP地址就可以完成通信了嘛?想象一下发qq消息的例子,有了IP地址能够把消息发送到对方的机器上,但是还需要有一个其他的标识来区分出,这个数据要给哪个程序进行解析.你怎么知道这个数据段给qq还是微信。

端口号(port)是传输层协议的内容.

  • 端口号是一个2字节16位的整数;
  • 端口号用来标识一个进程,告诉操作系统,当前的这个数据要交给哪一个进程来处理;
  • IP地址标识一台唯一的主机。
  • 端口号标识主机上唯一的一个进程。
  • IP地址+端口号能够标识网络上的某一台主机的某一个进程(标识全网唯一的进程)。
  • 一个端口号只能被一个进程占用.

网络通讯的本质就进程间通讯。也是进程到进程之间的通讯。两个进程都要看到同一份资源--网络。通讯就是在做IO(收数据或者发送数据)。

1.2.1.理解"端口号"和"进程ID"

我们之前在学习系统编程的时候,学习了pid表示唯一一个进程;此处我们的端口号也是唯一表示一个进程.那么这两者之间是怎样的关系?

为什么pid已经可以标识唯一一个进程了,还需要端口号呢?

  • a. 为了让系统和网络相互解耦。
  • b. 需要客户每一次都能找到服务器进程 -- 服务器的唯一性不能做任何改变 -- IP+port 可以保证不变,IP+pid不能保证不变。
  • c. 服务端的IP+port号 是不变的。
  • d. 不是所有的进程都需要提供网络服务,但是所有的进程都需要pid。

另外,一个进程可以绑定多个端口号;但是一个端口号不能被多个进程绑定;

在OS系统内部维护了一张哈希表,把port和pid地址关联起来。

1.2.2.理解源端口号和目的端口号

传输层协议(TCP和UDP)的数据段中有两个端口号,分别叫做源端口号和目的端口号.就是在描述"数据是谁发的,要发给谁"。

1.3.认识TCP/UDP协议

1.3.1.TCP协议

此处我们先对TCP(Transmission Control Protocol传输控制协议)有一个直观的认识;后面我们再详细讨论TCP的一些细节问题.

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

1.3.2.UDP协议

此处我们也是对UDP(User Datagram Protocol用户数据报协议)有一个直观的认识;后面再详细讨论

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据报

这里的可靠和不可靠是协议的特征无好坏贬义的意思。只是不同的协议表现出不同的特性。可靠是有成本的,可靠意味着复杂,不可靠意味着简单好用。

1.4.网络字节序

计算机在内存中存贮数据的时候分为大端和小端。

  • 大端就是把高权值的数据放在低地址处。
  • 小端就是把低权值的数据放在低地址处。(小小小)

而且每一个机器使用的机器字节序还可能不相同,在网络中发送数据的时候,是按照大端的顺序发送还是按照小端的顺序发送。接收者怎么知道这是大端还是小端。这就出问题了。

  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
  • 接收主机把1
  • 不管这台主机是大端机还是小端机,都会按照这个TCP/IP规定的网络字节序来发送/接收数据;如果当前发送主机是小端,就需要先将数据转成大端;否则就忽略,直接发送即可;

网络字节序和主机字节序的转换

 为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

  •  这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
  • 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。



2.socket编程接口

(IP+port号)称为socket(套接字)。

2.1.sockaddr结构

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX Domain Socket.然而,各种网络协议的地址格式并不相同.

  • IPv4IPv6的地址格式定义在netinet/in.h,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16 位端口号和32IP地址.
  • IPv4IPv6地址类型分别定义为常数AF_INETAF_INET6. 这样,只要取得某种sockaddr结构体的首地址, 不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
  • socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数;

struct sockaddr_in 的具体结构:

2.2.socket常见API

//创建socket文件描述符(TCP/UDP,客户端+服务器)
int socket(int domain, int type, int protocol);
//domain(域):选择使用本地通讯还是网络通讯。
        1.AF_UNIX/AF_LOCAL(域间通讯);
        2.AF_INET(使用ipv4协议);(AF_INET == PF_INET)
        3.AF_INET6(使用ipv6协议);
        4.其他不常用,不做介绍了。
//type:套接字提供服务的类型。
        1.SOCK_STREAM(提供流式服务)(对应TCP)
        2.SOCK_DGRAM(提供用户数据报套接字)(对应UDP)
        3.其他不常用
//protocol:
        1.具体协议的类型,但是一般默认写0 即可。因为一般我们在使用socket的时候前面两个参数写完之后默认使用的协议已经固定了,一般protocol这个参数设为0即可。

//返回值
        1.返回一个文件描述符(fd)
        2.如果失败返回-1,并且错误码被设定。
        3.以后的操作会变为文件或者类文件操作。读(read),写(write),关闭(close)




//绑定端口号(TCP/UDP,服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
//socket: socket返回的文件描述符。
//address: 绑定的 ip+port+协议家族,注意在填充struct sockaddr_in的时候应该为网络字节序(大端)。
//struct sockaddr_in 对象的长度。
//如果是虚拟机/独立真实的Linux环境,可以bind自己的公网IP。
//可以绑定自己的内网IP,但是作用不大,只能在局域网通讯。
//实际上一款网络服务器,不建议指明一个IP,一般都填充INADDR_ANY(0.0.0.0)。
//叫做任意地址绑定。


//按字节为单位向一块内存里面写零
void bzero(void* s, size_t n);//头文件<strings.h> / <cstrings>


//点分十进制 转化为 uint32_t网络字节序
in_addr_t inet_addr(const char *cp);
//注意这里输出in_addr_t == uint32_t 并且直接是网络字节序。



//udp读取数据
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
//flags==0:阻塞读取,有数据就读没数据就等待。
src_addr:发送方ip和port(输出型参数)
addrlen:发送方struct sockaddr结构体大小(输入输出型参数)


//网络中字节序uint32_t  转化为   点分十进制的字符串
char *inet_ntoa(struct in_addr in);


//udp发送数据,
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd:发送数据对应的文件描述符
buf+len 发送的数据
flags==0-:阻塞发送有数据就发,没数据就等。
dest_addr+addrlen : 目的IP和Port信息


//开始监听socket (TCP,服务器)
int listen(int socket, int backlog);

//接收请求(TCP,服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);

//建立连接(TCP,客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

2.3.点分十进制到init32_t之间的转化的原理

2.3.1. 12345   --->  “27.48.0.0”

int main()
{

    uint32_t ip = 12345; //对应 57.48.0.0
    struct _ip
    {
        unsigned char p1;
        unsigned char p2;
        unsigned char p3;
        unsigned char p4;
    };

    std::string str_ip;
    str_ip += std::to_string((int)((struct _ip *)&ip)->p1);
    str_ip += '.';
    str_ip += std::to_string((int)((struct _ip *)&ip)->p2);

    str_ip += '.';
    str_ip += std::to_string((int)((struct _ip *)&ip)->p3);

    str_ip += '.';
    str_ip += std::to_string((int)((struct _ip *)&ip)->p4);

    std::cout << str_ip << std::endl;
}

2.3.2. “27.48.0.0”   --->  12345 

int main()
{

    struct _ip
    {
        unsigned char p1;
        unsigned char p2;
        unsigned char p3;
        unsigned char p4;
    };

    std::string str_ip = "57.48.0.0";
    int posc = 0;
    auto pos = str_ip.find('.',0);
    int p1 = atoi(str_ip.substr(posc, pos).c_str());

    posc = pos;
    pos = str_ip.find('.', posc + 1);
    int p2 = atoi(str_ip.substr(posc+1, pos).c_str());

    posc = pos;
    pos = str_ip.find('.', posc + 1);
    int p3 = atoi(str_ip.substr(posc+1, pos).c_str());

    posc = pos;
    pos = str_ip.find('.', posc + 1);
    int p4 = atoi(str_ip.substr(posc+1, pos).c_str());

    struct _ip tmp ;
    tmp.p1 = p1;
    tmp.p2 = p2;
    tmp.p3 = p3;
    tmp.p4 = p4;

    std::cout<<*((uint32_t*)&tmp)<<std::endl;//12345
}

注意大小端不一样  可能对应的不一样。不建议自己转化。使用系统接口即可。

2.4.查看网络情况

2.4.1.netstat命令

参数:

  • -a:所有的
  • -u:udp协议对应的
  • -p:显示进程信息
  • -n:能显示数字的用数字替换。

 注意:

  • 127.0.0.1是本地环回地址,贯穿本机网络协议后在返回,用来测试。
  • 云服务器是虚拟化的服务器,不能直接绑定自己的公网ip。
  • 如果是虚拟机/独立真实的Linux环境,可以bind自己的公网IP。
  • 可以绑定自己的内网IP,但是作用不大,只能在局域网通讯。
  • 实际上一款网络服务器,不建议指明一个IP,一般都填充INADDR_ANY(0.0.0.0)。

2.5.udp协议实现网络字典

源码:

udp_server.hpp

#include <iostream>
#include <string>
#include <functional>
#include <cstring> //strerror
#include <cerrno>  //errno
#include <cstdlib> //exit
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

namespace server
{
    typedef std::function<void(int, std::string, uint16_t, std::string)> func_t;
    enum
    {
        USAGE_ERROR = 1,
        SOCKET_ERROR,
        BIND_ERROR,
        OPEN_ERROE,
        CATLINE_ERROR

    };

    class udpServer
    {

        // const static std::string defaultIp ;
        static const std::string defaultIp;

    public:
        udpServer(func_t func, const uint16_t &port, const std::string ip = defaultIp)
            : _port(port), _ip(ip), _sockfd(-1), _func(func)
        {
        }

        ~udpServer()
        {
        }

        void initServer()
        {
            // 创建套接字。
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sockfd < 0)
            { // 创建失败
                std::cerr << "socket error!! " << errno << ": " << strerror(errno) << std::endl;
                exit(SOCKET_ERROR);
            }

            // 绑定ip+port
            struct sockaddr_in local; // sockaddr_in 使用的时候要包含头文件 <netinet/in.h> 或者 <arpa/inet.h>
            bzero(&local, sizeof(local));
            // 填入 协议家族,端口号,ip地址(uint32_t类型的)
            local.sin_family = AF_INET;    // 指定协议家族
            local.sin_port = htons(_port); // 指定端口号 //注意主机字节序转化为网络字节序
            // local.sin_addr.s_addr =inet_addr(_ip.c_str());
            local.sin_addr.s_addr = INADDR_ANY; // 任意地址绑定 服务器的真实写法

            // 指定ip(uint32_t) //注意1.点分十进制转化为uint32_t; 2.主机字节序转化为网络字节序。

            int ret = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
            // 注意强制类型转化,(struct sockaddr*)
            if (ret == -1)
            {
                std::cerr << "bind error!! " << errno << ": " << strerror(errno) << std::endl;
                exit(BIND_ERROR);
            }
            // 初始化完成
        }

        void startServer()
        {
            // 服务器的本质就是一个死循环。
            // 死循环的代码也叫常驻内存进程
            // 只有死循环的进程,不退出的进程,才会在乎内存泄漏。
            char buf[1024];

            for (;;)
            {
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer); // 这里不能省去。
                int s = recvfrom(_sockfd, buf, sizeof buf, 0, (struct sockaddr *)&peer, &len);
                if (s > 0)
                {
                    // 读取成功,
                    buf[s] = 0;
                    // 数据再buf中,客户端的信息在peer中
                    // 将客户端信息转化出来
                    std::string clinetip = inet_ntoa(peer.sin_addr);
                    uint16_t clientport = ntohs(peer.sin_port);
                    std::string message = buf;
                    _func(_sockfd, message, clientport, clinetip);
                    // 这里设置了一个对调函数,实现通讯和业务逻辑解耦的操作。
                }
            }
        }

    private:
        uint16_t _port;  // server——端口号
        std::string _ip; // server——ip
        int _sockfd;     // socket的返回值的文件描述符。
        func_t _func;    // 设置回调函数
    };
    const std::string udpServer::defaultIp = "0.0.0.0";
    // 静态成员一定要在类外面进行初始化。

} // server end

udp_server.cc

#include "udp_server.hpp"
#include <unordered_map>
#include <fstream>
#include <memory>
#include <signal.h>
using namespace server;

std::unordered_map<std::string, std::string> dict; // 字典
std::string dicttxt = "./dict.txt";                // 配置文件
std::string sep = ":";                             // 分隔符

// 未来不同的udp服务器其实就是在这里不一样。业务逻辑的处理不一样。
void serverfunc(int sockfd, std::string message, uint16_t clientport, std::string clinetip)
{
    // 打印接受的数据
    std::cout << clinetip << "[" << clientport << "]#" << message << std::endl;

    struct sockaddr_in client_addr;
    bzero(&client_addr,sizeof(client_addr));
    client_addr.sin_family = AF_INET;
    client_addr.sin_port = htons(clientport);
    client_addr.sin_addr.s_addr = inet_addr(clinetip.c_str());
 
    std::string retstr;
    if (dict.end() == dict.find(message))
    {
        retstr = "没找到!!";
    }
    else
    {
        retstr = dict[message];
    }

    sendto(sockfd, retstr.c_str(), retstr.size(), 0, (struct sockaddr *)&client_addr, sizeof(client_addr));
    std::cout << "发送数据: " << retstr << std::endl;
}

static bool catline(const std::string &line, std::string *key, std::string *value)
{
    auto pos = line.find(sep);
    if (pos == std::string::npos)
    {
        return false;
    }
    *key = line.substr(0, pos);
    *value = line.substr(pos + sep.size());
    return true;
}

void dictinit()
{
    std::ifstream in(dicttxt, std::ios::binary);
    if (!in.is_open())
    {
        // 打开失败
        std::cerr << "open file" << dicttxt << "error!!" << std::endl;
        exit(OPEN_ERROE);
    }
    std::string line, key, value;
    while (getline(in, line))
    {
        if (catline(line, &key, &value))
        {
            dict.insert(make_pair(key, value));
        }
        else
        {
            std::cout << "catline error" << std::endl;
            exit(CATLINE_ERROR);
        }
    }
    in.close();
}

// test
void printdict()
{
    for (auto e : dict)
    {
        std::cout << e.first << "#" << e.second << std::endl;
    }
}

// 使用手册
void usage(char *proc)
{
    std::cout << "Usage: \n\t" << proc << " local_port\n\n";
}
void handler(int sig)
{
    //支持热加载。
    dictinit();
    std::cout<<"字典更新完成"<<std::endl;
}

// 未来将来吧 ,Ip和Port 传进来,我们需要用到,命令行参数。
// 使用 :"./server local_ip local_port"
int main(int argc, char *argv[])
{
    signal(3, handler);
    if (argc != 2)
    {
        usage(argv[0]);
        exit(USAGE_ERROR);
    }
    // //port
    uint16_t port = atoi(argv[1]);
    // uint16_t port = 10002;

    dictinit();
    // printdict();

    std::unique_ptr<udpServer> us(new udpServer(serverfunc, port)); // 不用传入ip,使用0.0.0.0
    us->initServer();
    us->startServer();

    return 0;
}

udp_client.hpp

#pragma once
#include <iostream>
#include <string>
#include <cassert>
#include <cstring>
#include <stdlib.h>
#include <cerrno>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>

namespace client
{
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR = 2,
        BIND_ERR = 3
    };
    class UdpClient
    {
    public:
        UdpClient(uint16_t serverport, std::string serverip)
            : _serverport(serverport), _serverip(serverip)
        {
        }

        void initclient()
        {
            int ret = socket(AF_INET, SOCK_DGRAM, 0);
            if (ret == -1)
            {
                std::cout << "socket error: " << errno << ":" << strerror(errno) << std::endl;
                exit(SOCKET_ERR); // 退出码是自己设置的。
            }
            _sockfd = ret;
            std::cout << "socket success: "
                      << " : " << _sockfd << std::endl;

            // 客户端不需要显示bind,//OS会自己绑定对应 的IP和port
            // 所有初始化任务完成。
        }

        void startclient()
        {
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));

            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr(_serverip.c_str()); // 1.string-》uint_t; 2.主机字节序-》网络字节序
            server.sin_port = htons(_serverport);                  // 主机转网络字节序。

            char buf[1024];
            while (true)
            {
                std::string message;
                std::cout << "Please Enter# ";
                std::cin >> message;
                sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
                std::cout << "发送数据:" << message << std::endl;

                struct sockaddr_in server;
                socklen_t len = sizeof(server);

                int s = recvfrom(_sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&server, &len);
                if (s > 0)
                {
                    buf[s] = 0;
                    std::cout << "接受数据:" << buf << std::endl;
                }
            }
        }

        ~UdpClient()
        {
        }

    private:
        std::string _serverip;
        uint16_t _serverport;
        int _sockfd;
    };

}

udp_client.cc

#include "udp_client.hpp"
#include <memory>
using namespace client;

// 使用手册
void usage(char *proc)
{
    std::cout << "Usage: \n\t" << proc << " server_ip server_port\n\n";
    // 客户端在发送消息的时候,使用的是服务端的公网IP
}

int main(int argc, char *argv[])
{

    if (argc != 3)
    {
        usage(argv[0]);
        exit(USAGE_ERR);
    }
    // uint16_t serverport = 10002;
    uint16_t serverport = atoi(argv[2]);
    std::string serverip = argv[1];

    std::unique_ptr<UdpClient> uc(new UdpClient(serverport, serverip)); // 不用传入ip,使用0.0.0.0
    uc->initclient();
    uc->startclient();
    return 0;
}

2.6.远端的shell解释器

使用的接口:

 #include <stdio.h>

 FILE *popen(const char *command, const char *type);//pipe+fork+exec
//执行传入的命令。执行结果以文件方式返回。
//command:未来的命令行字符串
//type:文件的打开方式 “r” “w” ---



int pclose(FILE *stream);
//读取执行结果之后需要关闭返回的文件描述符

 源码:

// 未来不同的udp服务器其实就是在这里不一样。业务逻辑的处理不一样。
//上述代码修改这里就可以修改整个服务器处理逻辑。
void serverfunc(int sockfd, std::string cmd, uint16_t clientport, std::string clinetip)
{
    // 打印接受的数据
    std::cout << clinetip << "[" << clientport << "]#" << cmd << std::endl;
    FILE* fp = popen(cmd.c_str(), "r");
    std::string retstr;
    char line[1024];
    while(fgets(line, sizeof(line)-1, fp))
    {
        retstr += line; 
    }

    struct sockaddr_in clinet_addr;
    bzero(&clinet_addr, sizeof(clinet_addr));
    clinet_addr.sin_family= AF_INET;
    clinet_addr.sin_port = htons(clientport);
    clinet_addr.sin_addr.s_addr = inet_addr(clinetip.c_str());

    sendto(sockfd, retstr.c_str(), retstr.size(),0 ,(struct sockaddr* )&clinet_addr, sizeof(clinet_addr));

    pclose(fp);
}

这就是一个原理版本的 shell(远端命令行解释器),我们写的只能说明原理,有些命令是不能执行的。

2.7.udp——实现网络聊天室

udp_sercer.cc  //替换对应的处理逻辑

// 未来不同的udp服务器其实就是在这里不一样。业务逻辑的处理不一样。
//替换上面的serverfunc函数即可实现不同的服务器处理替换。
void serverfunc(int sockfd, std::string message, uint16_t clientport, std::string clinetip)
{
    // 打印接受的数据
    // std::cout << clinetip << "[" << clientport << "]#" << cmd << std::endl;
    User user(clinetip, clientport);

    if (message == "online")
    {
        users.userAdd(user);
    }
    if (message == "delete")
    {
        users.userDelet(user);
    }

    if (users.userFind(user.getname()))
    {
        // 群发数据
        std::string retstr;
        retstr += user.getname();
        retstr += " ";
        retstr += std::to_string(time(NULL));
        retstr += " ";
        retstr += "#";
        retstr += message;
        users.allreply(sockfd, retstr);
    }
    else
    {
        // 用户没有登录 //单发数据
        std::string retstr;
        retstr += "你还没有登录,请先输入“online” 登录!!!";
        struct sockaddr_in peer;
        peer.sin_family = AF_INET;
        peer.sin_port = htons(user._port);
        peer.sin_addr.s_addr = inet_addr(user._ip.c_str());
        socklen_t len = sizeof(peer);
        sendto(sockfd, retstr.c_str(), retstr.size(), 0, (struct sockaddr *)&peer, len);
    }
}

usr_manege.hpp //用户结构体的构建和组织。

#pragma once

#include <iostream>
#include <string>
#include <unordered_map>

class User
{
public:
    User(std::string ip, uint16_t port)
        : _ip(ip), _port(port)
    {
        std::string str = std::to_string(_port);
        _username += _ip;
        _username += "[";
        _username += str;
        _username += "]";
    }

    std::string getname()
    {
        return _username;
    }
    std::string _ip;
    uint16_t _port;

private:
    std::string _username;
};

class Usermanager
{
public:
    Usermanager()
    {
    }
    ~Usermanager()
    {
    }
    void userAdd(User &val)
    {
        _map.insert(std::make_pair(val.getname(), val));
    }
    void userDelet(User &val)
    {
        _map.erase(val.getname());
    }

    std::unordered_map<std::string, User> getmap()
    {
        return _map;
    }

    bool userFind(const std::string &kay)
    {
        return _map.find(kay) != _map.end();
    }

    void allreply(int sockfd, std::string& message)
    {
        for (auto e : _map)
        {
            User user = e.second;
            struct sockaddr_in peer;
            peer.sin_family = AF_INET;
            peer.sin_port = htons(user._port);
            peer.sin_addr.s_addr = inet_addr(user._ip.c_str());
            socklen_t len = sizeof(peer);
            sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&peer, len);
        }
    }
private:
    std::unordered_map<std::string, User> _map;
};

udp_clinet.hpp  //

#pragma once
#include <iostream>
#include <string>
#include <cassert>
#include <cstring>
#include <stdlib.h>
#include <cerrno>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>

namespace client
{
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR = 2,
        BIND_ERR = 3
    };
    class UdpClient
    {
    public:
        UdpClient(uint16_t serverport, std::string serverip)
            : _serverport(serverport), _serverip(serverip)
        {
        }

        void initclient()
        {
            int ret = socket(AF_INET, SOCK_DGRAM, 0);
            if (ret == -1)
            {
                std::cout << "socket error: " << errno << ":" << strerror(errno) << std::endl;
                exit(SOCKET_ERR); // 退出码是自己设置的。
            }
            _sockfd = ret;
            std::cout << "socket success: "
                      << " : " << _sockfd << std::endl;

            // 客户端不需要显示bind,//OS会自己绑定对应 的IP和port
            // 所有初始化任务完成。
        }

        static void *readfunc(void *arg)
        {
            int sockfd = *(static_cast<int *>(arg));
            char buf[1024];

            struct sockaddr_in server;
            socklen_t len = sizeof(server);
            while (true)
            {
                int s = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&server, &len);
                if (s > 0) 
                {
                    buf[s] = 0;
                    std::cout << buf << std::endl;
                }
            }
        }
        void startclient()
        {
            pthread_create(&_readpid, nullptr, readfunc, &_sockfd);
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));

            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr(_serverip.c_str()); // 1.string-》uint_t; 2.主机字节序-》网络字节序
            server.sin_port = htons(_serverport);                  // 主机转网络字节序。

            char buf[1024];
            while (true)
            {
                std::string message;
                std::cerr << "Please Enter# ";
                getline(std::cin, message);
                sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
            }
        }

        ~UdpClient()
        {
        }

    private:
        std::string _serverip;
        uint16_t _serverport;
        int _sockfd;

        pthread_t _readpid;
    };

}

不同的客户端对应的窗口,所使用的ip+port不一样。

2.8.Windows版本的的网络套接字

对上面的网络字典代码写一个windows客户端:

win_udp_clinet.cpp

//注意此代码要拷贝到windows下编译即可

#include<iostream>
#include<string>

//首先win打Linux下的sock接口都是一样的。无差别。
//只有四点不同
// 1. win需要包含头文件 <winSock2.h>
// 2. 引入库 #pragma comment(lib, "ws2_32.lib")
// 3. 开始初始化winsock和启动winsock; 最后关闭winsock 
// 4. SOCKET  相当于 int  也就是Linux中打开sock 返回的文件描述符

#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")


std::string ip = "82.157.245.253";
uint16_t port = 8080;


int main()
{
	//初始化winsock
	WSAData wsd;
	//启动winsock
	if (WSAStartup(MAKEWORD(2, 2), &wsd))
	{
		std::cout << "WSAStartup error!!" << std::endl;
		return 0;
	}
	else
	{
		std::cout << "WSAStartup success!!" << std::endl;
	}


	SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);//IPPROTO_UDP  //默认是0即可,也可也写上对应的宏。
	if (sock == SOCKET_ERROR)//这里SOCKET_ERROR的值其实就是-1 ,和我们以前LInux的套接字一样
	{
		std::cout << "socket error !!" << WSAGetLastError() << std::endl;
		return 1;
	}
	else
	{
		std::cout << "socket success!!" << std::endl;
	}
	struct sockaddr_in server;
	memset(&server, 0, sizeof(server));

	server.sin_family = AF_INET;
	//server.sin_addr.s_addr = inet_addr(ip.c_str()); // 1.string-》uint_t; 2.主机字节序-》网络字节序
	server.sin_addr.S_un.S_addr = inet_addr(ip.c_str());
		// 1.string-》uint_t; 2.主机字节序-》网络字节序
	server.sin_port = htons(port);                  // 主机转网络字节序。

	char buf[1024];
	while (true)
	{
		std::string message;
		std::cout << "Please Enter# ";
		std::getline(std::cin, message);
		sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));
		std::cout << "发送数据:" << message << std::endl;

		struct sockaddr_in server;
		int len = sizeof(server);

		int s = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&server, &len);
		if (s > 0)
		{
			buf[s] = 0;
			std::cout << "接受数据:" << buf << std::endl;
		}
	}

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

	//关闭winsock
	WSACleanup();
	
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/621330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

8B/10B编码

8B/10B编码 8B/10B编码介绍实现方式8b/10b编码中的编码表和字节编码RD控制符号 8B/10B编码 介绍 8b/10b编码是将8位符号映射到10位符号&#xff0c;以实现直流平衡&#xff0c;同时提供足够多的状态来实现时钟恢复。这意味着在一个至少20位的字符串中&#xff0c;1和0的计数之…

[论文阅读笔记74]The Power of Scale for Parameter-Efficient Prompt Tuning

1. 基本信息 题目论文作者与单位来源年份The Power of Scale for Parameter-Effificient Prompt TuningBrian Lester等googleConference on Empirical Methods in Natural Language Processing2021 857 Citations 论文链接&#xff1a;https://arxiv.org/abs/2104.08691 论…

配置SSH远程登录和免密登录

上一篇我们已经讲了如何配置修改Linux的主机名和网络设置&#xff0c;这一篇我们来讲一下配置Linux的SSH免密登录。   首先讲一下我们为什么要配置SSH 免密登录&#xff0c;通过VMware Workstation工具操作虚拟机十分不方便&#xff0c;无法复制内容到虚拟机中&#xff0c;也…

Python使用多进程并行加速业务操作 完整代码

Python使用多进程并行加速业务操作 完整代码 需求分析 完整代码 本demo性能分析 Python中单线程、多线程和多进程的效率对比实验 需求分析 最近在对一个数据集进行处理&#xff0c;共2000条&#xff0c;每条去调一个第三方接口&#xff0c;耗时7-10秒。单线程处理一次要3.…

Vue - 项目编译速度、性能优化、打包体积优化

GitHub Demo 地址 在线预览 Vue - 项目编译速度、性能优化、打包体积优化 序一、编译速度优化1、使用缓存1.1、缓存插件 - HardSourceWebpackPlugin1.2、webpack5 配置cache1.3、cache-loader 插件 2、合理使用source-map3、多线程打包3.1、thread-loader3.2、parallel-webpac…

阿里5面,成功唬住面试官拿了21K,面试也没有那么难吧....

阿里的面试挺独特&#xff0c;每轮面试都没有 HR 约时间&#xff0c;一般是晚上 8 点左右面试官来一个电话&#xff0c;问是否能面试&#xff0c;能的话开始面&#xff0c;不能就约一个其它时间。 全程 5 面&#xff0c;前四面技术面&#xff0c;电话面试&#xff0c;最后一面…

吴恩达 ChatGPT Prompt Engineering for Developers 系列课程笔记--08 Chatbot

08 Chatbot ChatGPT的一种重要功能是作为一个聊天机器人&#xff0c;本节将展示如何和ChatGPT进行对话 1) 不同的角色&#xff08;Roles&#xff09; 前面几节的课程中&#xff0c;我们通过如下函数调用ChatGPT的接口&#xff0c;输入用户输入的prompt&#xff0c;返回模型生…

第二章硬件入门之电容

第二章硬件入门之电容 文章目录 第二章硬件入门之电容一、电容是什么&#xff1f;二、实际应用场景常见电容&#xff1a;1.陶瓷电容&#xff08;无正负极之分&#xff09;1、旁路2、去耦 2.铝电解电容贴片式插件式3.安规电容x电容Y电容 总结 一、电容是什么&#xff1f; **电容…

【kernel exploit】CVE-2022-2602 UNIX_GC错误释放io_uring注册的file结构-UAF

本文主要参考 [漏洞分析] CVE-2022-2602 io_uring UAF内核提权详细解析 并做一些补充。 影响版本&#xff1a;Linux Kernel < v6.0.3。v6.0.3已修复。 测试版本&#xff1a;Linux-v6.0.2 &#xff08;v6.0.2 测试失败&#xff0c;v5.18.19测试成功&#xff09; exploit及测…

React Fiber 使用 MessageChannel + requestAnimationFrame 模拟实现 requestIdleCallback

由于 requestIdleCallback 兼容性较差且不支持 Safari&#xff0c;React Fiber 需要实现一个 requestIdleCallback polyfill 做浏览器兼容&#xff1b; MDN RequestIdleCallbackMDN RequestAnimationFrameMDN MessageChannel 以下为其使用 MessageChannel requestAnimationF…

vivado中ila的使用方法记录

ILA工具生成方法 一、 ILA工具介绍 在FPGA的开发中&#xff0c;当完成代码设计后&#xff0c;为了验证代码的准确性和各种不同条件下的可靠性&#xff0c;往往需要优先想到通过逻辑仿真进行相关验证。使用逻辑仿真进行验证虽然可以周密的考虑给出不同输入条件下的输出结果或交…

“智慧赋能 强链塑链”—— 汽车行业供应链管理数字化应用探讨

01车企供应链数字化的必要性 汽车供应链是一个复杂的系统&#xff0c;很多汽车企业因为供应链管理不当&#xff0c;造成资源浪费、成本高、客户满意度低等一系列问题&#xff1b;而汽车行业规模技术门槛高、配合协同复杂的特性&#xff0c;决定了其供应链缺口无法在短时间内填…

Three.js系列-报错export ‘Geometry‘ (imported as ‘THREE‘) was not found in ‘three‘

今天遇到报错export ‘Geometry’ (imported as ‘THREE’) was not found in ‘three’ port Geometry (imported as THREE) was not found in three (possible exports: ACESFilmicToneMapping, AddEquation, AddOperation, AdditiveAnimationBlendMode, AdditiveBlending, …

为什么大家都不用postman而选择 Apifox呢?

丢掉 Postman&#xff0c;Apifox 更香 作为开发者&#xff0c;丢掉 Postman 和 Jmeter吧&#xff0c;这款国产 API 工具更香&#xff0c;更安全&#xff01;一键即可导入 Postman 数据&#xff01; 一、Apifox 是什么&#xff1f; 1、Apifox 定位 Apifox Postman Swagger …

Altium Designer(AD)局域网内使用解冲突

1. Altium Designer 版本 AD15.0.8&#xff0c;电路设计软件&#xff0c;硬件工攻城狮必备技能&#xff0c;软件攻城狮也要会一点点 2. AD软件出现“Your license is already used on computer “LAPTOP-F99R6OR1” using product “AltiumDesigner” 用同事的安装包解压安装的…

Mysql 索引详细解析——底层->应用

1、索引的数据结构 1.1 概述 索引&#xff08;index&#xff09;是帮助Mysql高效获取数据的数据结构。 索引的本质&#xff1a; 索引是数据结构。简单理解为“排好序的快速查找数据结构”&#xff0c;满足特定查找算法。这些数据结构以某种方式指向数据&#xff0c; 这样就可…

期末计算机网络知识点总结

开篇提示: 因为博主图床使用的是GitHub进行存储的&#xff0c;因此当出现图片无法加载&#xff0c;可以尝试更换网络&#xff0c;或者用其他上网方法。当时想用 Gitee进行存储&#xff0c;但一直失败&#xff0c;过一段时间再试几次。目前这篇博文会一直更新&#xff0c;补充新…

Python的内置函数和保留字(关键字)

目录 内置函数 关键字&#xff08;保留字&#xff09; 内置函数 Python 解释器自带的函数叫做内置函数&#xff0c;这些函数可以直接使用&#xff0c;不需要导入某个模块。 将使用频繁的代码段封装起来&#xff0c;并给它起一个名字&#xff0c;以后使用的时候只要知道名字就可…

虹科分享 | 《面向金融行业的商业智能解决方案》白皮书

在以数据为中心的商业环境中&#xff0c;金融业面临着众多挑战&#xff0c;包括瞬息万变的市场动态、监管要求以及客户期望。金融行业从各种来源生成大量数据&#xff0c;包括交易、客户互动、市场数据和内部运营等。然而&#xff0c;如果缺乏有效的数据管理和分析&#xff0c;…

算法修炼之筑基篇——筑基二层后期(初步理解解决贪心算法)

✨博主&#xff1a;命运之光 &#x1f984;专栏&#xff1a;算法修炼之练气篇 &#x1f353;专栏&#xff1a;算法修炼之筑基篇 ✨博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;学习了算法修炼之练气篇想必各位蒟蒻们的基础已经非常的扎实了&#xff0c;下来…