目录

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)是传输层协议的内容.

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

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

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

为什么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.网络字节序

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

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

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

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

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

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



2.socket编程接口

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

2.1.sockaddr结构

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

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

struct sockaddr_in 的具体结构:

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

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命令

参数:

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

 注意:

2.5.udp协议实现网络字典

源码:

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

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

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

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(远端命令行解释器),我们写的只能说明原理,有些命令是不能执行的。

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

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

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

}

【计算机网络】网络编程套接字(一)-LMLPHP不同的客户端对应的窗口,所使用的ip+port不一样。

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

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

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