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

这篇具有很好参考价值的文章主要介绍了【计算机网络】网络编程套接字(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

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文章来源地址https://www.toymoban.com/news/detail-475321.html

//注意此代码要拷贝到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;
}

到了这里,关于【计算机网络】网络编程套接字(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 计算机网络套接字编程实验-TCP多进程并发服务器程序与单进程客户端程序(简单回声)

    1.实验系列 ·Linux NAP-Linux网络应用编程系列 2.实验目的 ·理解多进程(Multiprocess)相关基本概念,理解父子进程之间的关系与差异,熟练掌握基于fork()的多进程编程模式; ·理解僵尸进程产生原理,能基于|sigaction()或signal(),使用waitpid()规避僵尸进程产生; ·

    2024年02月12日
    浏览(45)
  • 计算机网络套接字编程实验-TCP单进程循环服务器程序与单进程客户端程序(简单回声)

    1.实验系列 ·Linux NAP-Linux网络应用编程系列 2.实验目的 ·理解并掌握在程序运行时从命令行读取数据的C语言编程方法; ·理解并掌握基于命令参数设置并获取IP与Port的C语言编程方法; ·理解并掌握套接字地址的数据结构定义与地址转换函数应用; ·理解并掌握网络字节序

    2024年02月11日
    浏览(93)
  • 计算机网络--网络编程(1)

    简单认识一下传输层中的UDP和TCP: TCP:有链接,可靠传输,面向字节流,全双工 UDP:无连接,不可靠传输,面向数据报,全双工 有链接类似于打电话,通了就是有链接。没通就一直在等待。 无连接类似于发短信,只管发,不管到。 可靠传输就是保证信息传输的可靠性。就

    2024年02月11日
    浏览(43)
  • 【计算机网络】4 Socket网络编程

    目录 写在前面的话 概览 环境 URL请求程序: 2. 系统时间查询 服务端 T_TCPServer.py代码 客户端 T_TCPClient.py代码 运行效果 3. 网络文件传输 服务端 TF_TCPServer.py代码 运行效果(后面加了远程功能,效果图暂时还在本地) 4. 网络聊天室 服务端 UDPServer.py代码 客户端 UDPClient.py代码 运

    2024年02月01日
    浏览(41)
  • 【计算机网络】Socket编程

    IP地址:公网IP,用于唯一标识互联网中的一台主机 源IP,目的IP:对于一个报文来讲,从哪来,到哪去。 源IP指将数据发送过来的IP地址,目的IP指将数据发送给下一个设备的IP地址(mac地址的变化) 意义: 指导一个报文该如何进行路径选择,目的IP是让我们根据目标进行路径选

    2024年02月08日
    浏览(47)
  • 【计算机网络】socket编程基础

    因特网上的每台计算机都有一个唯一的IP地址,如果一台主机上的数据要传输到另一台主机,那么对端主机的IP地址就应该作为该数据传输时的目的IP地址。但仅仅知道目的IP地址是不够的,当对端主机收到数据后,对端还需要对该主机作出相应,因此对端主机也需要发送数据

    2024年02月15日
    浏览(47)
  • 计算机网络技术与JAVA网络编程URL编程-----JAVA入门基础教程-----计算机网络经典

    import org.junit.jupiter.api.Test; import java.io.*; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; public class URLTest { public static void main(String[] args) { //URL:统一资源定位符(种子),一个URL就定位着互联网上某个资源的地址 //http:应用层协议,IP地址,端口号,资源地址,参数

    2024年02月15日
    浏览(57)
  • 计算机网络技术与JAVA网络编程UDP编程-----JAVA入门基础教程-----计算机网络经典

    import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.*; public class UDP { public static void main(String[] args) { DatagramSocket datagramSocket = null; try { datagramSocket = new DatagramSocket(); InetAddress inetAddress = InetAddress.getByName(\\\"127.0.0.1\\\"); int port = 9090; byte[] byte

    2024年02月15日
    浏览(49)
  • 【计算机网络】网络编程接口 Socket API 解读(8)

             Socket 是网络协议栈暴露给编程人员的 API,相比复杂的计算机网络协议,API 对关键操作和配置数据进行了抽象,简化了程序编程。         本文讲述的 socket 内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解 socket 编程。        本文主要描述

    2024年02月08日
    浏览(44)
  • 【计算机网络】网络编程接口 Socket API 解读(11)

             Socket 是网络协议栈暴露给编程人员的 API,相比复杂的计算机网络协议,API 对关键操作和配置数据进行了抽象,简化了程序编程。         本文讲述的 socket 内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解 socket 编程。 遵循 POSIX.1-2008      

    2024年02月08日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包