[Computer Network] Network programming socket (1)

Table of contents

1. Preliminary knowledge

1.1. Understanding source IP address and destination IP address

1.2. Know the port number

1.2.1. Understanding "port number" and "process ID"

1.2.2. Understanding the source port number and destination port number

1.3. Understand the TCP/UDP protocol

1.3.1.TCP protocol

1.3.2. UDP protocol

1.4. Network byte order

Conversion between network byte order and host byte order

2. socket programming interface

2.1.sockaddr structure

The specific structure of struct sockaddr_in:

2.2.socket common API

2.3. The principle of conversion between dotted decimal and init32_t

2.3.1. 12345   --->  “27.48.0.0”

2.3.2. “27.48.0.0”   --->  12345 

2.4. Check the network status

2.4.1. netstat command

2.5. UDP protocol implements network dictionary

​edit

2.6. Remote shell interpreter

2.7.udp——realize the network chat room

2.8.Windows version of network socket




1. Preliminary knowledge

1.1. Understanding source IP address and destination IP address

In the header of the IP data packet, there are two IP addresses, called the source IP address and the destination IP address respectively. Determined directionality during routing. Provide direction for each stage of forwarding.

1.2. Know the port number

Thinking: Can we complete the communication only with the IP address? Imagine the example of sending qq messages. With the IP address, the message can be sent to the other party’s machine, but there needs to be another identifier to distinguish it. This data Which program should be analyzed. How do you know whether this data segment is for QQ or WeChat.

The port number (port) is the content of the transport layer protocol.

  • The port number is a 2-byte 16-bit integer;
  • The port number is used to identify a process and tell the operating system which process the current data should be handed over to;
  • An IP address identifies a unique host.
  • The port number uniquely identifies a process on the host.
  • The IP address + port number can identify a certain process of a certain host on the network (identify the unique process of the entire network).
  • A port number can only be occupied by one process.

The essence of network communication is inter-process communication. It is also process-to-process communication. Both processes must see the same resource - the network. Communication is doing IO (receiving data or sending data).

1.2.1. Understanding "port number" and "process ID"

When we were learning system programming before, we learned that pid represents the only process; here our port number also uniquely represents a process. So what is the relationship between the two?

Why pid can already identify the only process, but also need the port number?

  • a. In order to decouple the system and the network from each other.
  • b. The client needs to be able to find the server process every time -- the uniqueness of the server cannot be changed -- IP+port can be guaranteed to remain unchanged, but IP+pid cannot be guaranteed to remain unchanged.
  • c. The IP+port number of the server is unchanged.
  • d. Not all processes need to provide network services, but all processes need pid.

In addition, a process can bind multiple port numbers; but a port number cannot be bound by multiple processes;

A hash table is maintained inside the OS system to associate the port with the pid address.

1.2.2. Understanding the source port number and destination port number

There are two port numbers in the data segment of the transport layer protocol (TCP and UDP), which are called the source port number and the destination port number. It is to describe "who sent the data and who to send it to".

1.3. Understand the TCP/UDP protocol

1.3.1.TCP protocol

Here we first have an intuitive understanding of TCP (Transmission Control Protocol); later we will discuss some details of TCP in detail .

  • transport layer protocol
  • connected
  • reliable transmission
  • stream-oriented

1.3.2. UDP protocol

Here we also have an intuitive understanding of UDP (User Datagram Protocol User Datagram Protocol); we will discuss it in detail later

  • transport layer protocol
  • no connection
  • unreliable transmission
  • Datagram Oriented

Reliable and unreliable here are the characteristics of the agreement and have no derogatory meaning. It's just that different protocols exhibit different characteristics. Reliability comes at a cost, reliability means complexity, and unreliability means simplicity and ease of use.

1.4. Network byte order

When a computer stores data in memory, it is divided into big endian and little endian.

  • The big end is to put high-value data at low addresses.
  • Little-endian is to put low-weight data at low addresses. (little little)

Moreover, the machine byte order used by each machine may be different. When sending data in the network, whether to send it in the order of big endian or in order of little endian. How does the receiver know if this is big endian or little endian. Here comes the problem.

  • The sending host usually sends the data in the sending buffer in the order of memory addresses from low to high;
  • The receiving host puts 1
  • Regardless of whether the host is a big-endian or a little-endian, it will send/receive data according to the network byte order specified by this TCP/IP; if the current sending host is a little-endian, it needs to convert the data to a big-endian first; otherwise Just ignore it and send it directly;

Conversion between network byte order and host byte order

 In order to make the network program portable, so that the same C code can run normally after compiling on big-endian and little-endian computers, the following library functions can be called to convert the network byte order and the host byte order.

  •  These function names are easy to remember, h means host, n means network, l means 32-bit long integer, s means 16-bit short integer.
  • For example, htonl means converting a 32-bit long integer from host byte order to network byte order, for example, converting an IP address and preparing to send it.
  • If the host is little-endian, these functions convert the parameters accordingly and return;
  • If the host is big-endian, these functions do not convert and return the arguments unchanged.



2. socket programming interface

(IP+port number) is called socket (socket).

2.1.sockaddr structure

The socket API is an abstract network programming interface, applicable to various underlying network protocols, such as IPv4, IPv6, and UNIX Domain Socket to be discussed later. However, the address formats of various network protocols are not the same.

  • The address format of IPv4 and IPv6 is defined in netinet/in.h , and IPv4 address is represented by sockaddr_in structure , including 16 -bit address type , 16-bit port number and 32 -bit IP address .
  • IPv4 and IPv6 address types are respectively defined as constants AF_INET and AF_INET6. In this way , as long as the first address of a certain sockaddr structure is obtained , it is not necessary to know which type of sockaddr structure it is , and the address type field in the structure can be determined. content .
  • The socket API can be represented by the struct sockaddr * type, which needs to be converted into sockaddr_in when used; the advantage of this is the versatility of the program, which can receive IPv4, IPv6, and various types of sockaddr structure pointers of UNIX Domain Socket as parameter;

The specific structure of struct sockaddr_in:

2.2.socket common 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. The principle of conversion between dotted decimal and 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
}

Note that the big and small ends may correspond to different ones. It is not recommended to convert by yourself. Just use the system interface.

2.4. Check the network status

2.4.1. netstat command

parameter:

  • -a: all
  • -u: corresponding to the udp protocol
  • -p: display process information
  • -n: Replace numbers that can display numbers.

 Notice:

  • 127.0.0.1 is the local loopback address, which is returned after running through the local network protocol for testing.
  • The cloud server is a virtualized server and cannot be directly bound to its own public network ip.
  • If it is a virtual machine/independent real Linux environment, you can bind your own public IP.
  • You can bind your own intranet IP, but it has little effect and can only communicate in the LAN.
  • In fact, for a web server, it is not recommended to specify an IP, and it is generally filled with INADDR_ANY(0.0.0.0).

2.5. UDP protocol implements network dictionary

source code:

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. Remote shell interpreter

Interface used:

 #include <stdio.h>

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



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

 source code:

// 未来不同的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);
}

This is a principle version of the shell (remote command line interpreter). What we write can only explain the principle, and some commands cannot be executed.

2.7.udp——realize the network chat room

udp_sercer.cc //Replace the corresponding processing logic

// 未来不同的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 //Construction and organization of user structures.

#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;
    };

}

The windows corresponding to different clients use different ip+port.

2.8.Windows version of network socket

Write a windows client for the above network dictionary code:

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;
}

Supongo que te gusta

Origin blog.csdn.net/zxf123567/article/details/130882996
Recomendado
Clasificación