台州网站策划台州网站策划,pinterest官网入口,怎么通过贷款网站找做贷款客户,开一家网店前言#xff1a;本节内容将带友友们实现一个UDP协议的聊天室。 主要原理是客户端发送数据给服务端。 服务端将数据再转发给所有链接服务端的客户端。 所以#xff0c; 我们主要就是要实现客户端以及服务端的逻辑代码。 那么#xff0c; 接下来开始我们的学习吧。 ps:本节内容… 前言本节内容将带友友们实现一个UDP协议的聊天室。 主要原理是客户端发送数据给服务端。 服务端将数据再转发给所有链接服务端的客户端。 所以 我们主要就是要实现客户端以及服务端的逻辑代码。 那么 接下来开始我们的学习吧。 ps:本节内容建议了解socket套接字的接口的友友们进行观看哦本节内容中涉及到的接口都不会讲解 直接就用了。 目录 整体代码
Udpclient
UdpServer
main配合UdpServer UdpServer的入口
准备文件
实现步骤
实现服务端客户端的收发消息
Udpserver
Init函数
run函数 UdpServer析构
Udpclient
实现客户端之间的聊天功能
Udpserver
Udpclient
运行结果 整体代码 先上整体代码
Udpclient #includeiostream
using namespace std;
#includestring
#includesys/types.h
#includeLog.hpp
#includesys/socket.h
#includepthread.h
#includearpa/inet.h
#includestring.h
#includenetinet/in.h
Log lg;class ThreadData
{
public:sockaddr_in server;int sockfd;
};void* recv_message(void* args)
{char buffer[1024];ThreadData* td static_castThreadData*(args); while (true){//接收数据sockaddr_in temp;socklen_t len;string info;ssize_t s recvfrom(td-sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr*)temp, len);if (s 0){lg(Error, recv error, error: %d, strerror: %s, errno, strerror(errno));continue;}buffer[s] 0;info buffer;cout info endl;}}void* send_message(void* args)
{ThreadData* td static_castThreadData*(args);string message;while (true){ getline(cin, message); //获取数据//发送数据sendto(td-sockfd, message.c_str(), message.size(), 0, (sockaddr*)td-server, sizeof(td-server));}
}int main(int argc, char* argv[])
{if (argc ! 3){cout Client server endl;}//先拿到套接字的参数string serverip argv[1];uint16_t serverport stoi(argv[2]);ThreadData td;//创建套接字与打开网卡memset(td.server, 0, sizeof(td.server));td.server.sin_family AF_INET;td.server.sin_port htons(serverport);td.server.sin_addr.s_addr inet_addr(serverip.c_str());td.sockfd socket(AF_INET, SOCK_DGRAM, 0); //创建文件描述符, 网卡的文件描述符 网络传输就是使用网络文件描述符找到对应的文件内的数据if (td.sockfd 0){lg(Error, client create sockfd error, errno: %d, strerror: %s, errno, strerror(errno));exit(1);}//创建线程 然后运行线程 等待线程pthread_t recv, send;pthread_create(recv, nullptr, recv_message, td);pthread_create(send, nullptr, send_message, td);pthread_join(recv, nullptr);pthread_join(send, nullptr);close(td.sockfd);return 0;
}
UdpServer
#includeiostream
using namespace std;
#includesys/types.h
#includestring
#includesys/socket.h
#includearpa/inet.h
#includestring.h
#includestrings.h
#includeLog.hpp
#includefunctional
#includenetinet/in.h
#includeunordered_mapint defaultport 8080;
string defaultip 0.0.0.0;using func_t functionstring(string, sockaddr_in, unordered_mapstring, sockaddr_in);
Log lg;enum
{SockError 2,BindError 3,RecvError 4,
};class UdpServer
{
public:UdpServer(uint16_t port defaultport) : port_(port), ip_(defaultip), isrunning_(false){}void Init(){//先创建套接字变量并且完成初始化。 然后就创建网卡文件sockaddr_in local;memset(local, 0, sizeof(local));local.sin_family AF_INET;local.sin_port htons(port_);local.sin_addr.s_addr inet_addr(ip_.c_str());sockfd_ socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ 0){lg(Fatal, create sock error, errno: %d, strerror: %s, errno, strerror(errno));exit(SockError);}lg(Info, create sock success);//绑定if (bind(sockfd_, (sockaddr*)local, sizeof(local)) 0) {lg(Fatal, bind error, errno: %d, strerror: %s, errno, strerror(errno));exit(BindError);}lg(Info, bind success);}void BroadCast(string message, unordered_mapstring, sockaddr_in clients){cout 1 endl;for (auto e : clients){sendto(sockfd_, message.c_str(), message.size(), 0, (sockaddr*)e.second, sizeof(e.second));}cout 2 endl;}void run(func_t func){isrunning_ true;char inbuffer[1024];while (isrunning_){memset(inbuffer, 0, sizeof(inbuffer));sockaddr_in client;socklen_t client_len;memset(client, 0, sizeof(client));//接收数据的同时监听到客户端的来源ssize_t s recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (sockaddr*)client, client_len);if (s 0) {lg(Waring, recvfrom error, errno: %d, strerror: %s, errno, strerror(errno));continue;}inbuffer[s] 0;//处理数据//创建套接字 用来监听是哪一个客户端string message inbuffer;message func(message, client, clients);//处理完成后 返回发送给客户端BroadCast(message, clients);// sendto(sockfd_, message.c_str(), message.size(), 0, (sockaddr*)client, sizeof(client));}}~UdpServer(){if (sockfd_ 0) close(sockfd_); }
private:int sockfd_;uint16_t port_;string ip_;bool isrunning_;unordered_mapstring, sockaddr_in clients;};
main配合UdpServer UdpServer的入口 #includeUdpServer.hpp
#includememorystring Handler(string message, sockaddr_in client, unordered_mapstring, sockaddr_in clients)
{string tmp inet_ntoa(client.sin_addr) to_string(client.sin_port);if (!clients.count(tmp)){clients[tmp] client; cout ip inet_ntoa(client.sin_addr) : port client.sin_port has add in talk room endl;}message [ string(inet_ntoa(client.sin_addr)) : to_string(client.sin_port) ]#: message; return message;
}int main(int argc, char* argv[])
{if (argc ! 2){cout has return endl;return 1;}//uint16_t serverport stoi(argv[1]); unique_ptrUdpServer svr(new UdpServer(serverport));//svr-Init();svr-run(Handler);return 0;
}
准备文件 我们要准备三个文件 Udpclient.cc——用来运行起来客户端 UdpServer.hpp——用来实现服务端的各种接口 main.cc——用来运行起来服务端 除了这三个主要的文件。 其实博主还准备了两个可以忽略的文件为了方便。 一个是博主自己写的日志程序 用来打印日志。 一个是makefile 方便编译。 如果没有日志程序的话打印错误信息时直接cout printf打印即可。 makefile建议带上 方便编译养成好习惯。 实现步骤
注意 一步到位是很难的。 所以我们先实现简单的功能 再实现困难的功能。 这里简单的功能就是先让客户端能够将数据发给服务端了 然后服务端接收到消息后再将数据返回给客户端。 这里困难的功能就是当多个客户端如何看到互相的信息。然后如何能够一遍发信息一边收信息。
实现服务端客户端的收发消息
Udpserver 实现逻辑Udpserver.hpp中封装一个类。这个类里面封装一些接口 然后我们在main函数中创建类对象 在执行接口操作。 所以 先封装一个类 将要实现的接口以及要用到的变量写上 实现一个框架
#includeiostream
using namespace std;
#includestringint defaultport 8080; //默认的端口号我们要创建一个默认的端口号
string defaultip 0.0.0.0; //在服务器中使用套接字的时候 bind函数不能绑定公网IP 因为
//服务器的公网IP可能是虚拟的 注意IP地址是和网卡挂钩的 一个网卡只能有一个IP地址。
//绑定ip地址就是说在绑定网卡也就是说绑定某个IP地址后就只能监听这一个网卡的消息了。 但是
//有些机器是有很多张网卡的 所以就有一个默认IP:0.0.0.0 绑定这个IP就能监听在本机器下面
//所有的网卡的信息。 Log lg;class UdpServer
{
public:UdpServer(uint16_t port defaultport) : port_(port), ip_(defaultip), isrunning_(false){}void Init(){}void run(func_t func){}~UdpServer(){}
private:int sockfd_; //服务端的网卡文件的编号uint16_t port_; //服务器起来后的端口号string ip_; //服务器起来的时候所在的ip地址bool isrunning_; //服务器是否正在运行}; 下面是main.cpp里面的内容 直接启动服务端。
#includeUdpServer.hpp
#includememoryint main(int argc, char* argv[])
{if (argc ! 2){cout has return endl;return 1;}//uint16_t serverport stoi(argv[1]); unique_ptrUdpServer svr(new UdpServer(serverport));//svr-Init();svr-run();return 0;
}
Init函数 Udpserver里面的Init函数 这个函数用来绑定服务端的套接字的。 什么是绑定 博主目前的理解就是将我们运行的服务端这个程序能够和网卡建立起关系。 这个关系中 关系的两端是我们运行的服务端程序和socket网卡文件网卡文件就代表了网卡。关系的纽带是ip地址和端口号。 利用ip地址和端口号来将我们的服务端程序绑定给网卡 这个时候因为网卡的工作性质 其他的进程都不能再绑定网卡了 直到我们的服务端退出。 void Init(){//先创建套接字变量并且完成初始化。 然后就创建网卡文件sockaddr_in local;memset(local, 0, sizeof(local));local.sin_family AF_INET;local.sin_port htons(port_);local.sin_addr.s_addr inet_addr(ip_.c_str());sockfd_ socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ 0){lg(Fatal, create sock error, errno: %d, strerror: %s, errno, strerror(errno));exit(SockError);}lg(Info, create sock success);//绑定if (bind(sockfd_, (sockaddr*)local, sizeof(local)) 0) {lg(Fatal, bind error, errno: %d, strerror: %s, errno, strerror(errno));exit(BindError);}lg(Info, bind success);}run函数 我们这里思考一个问题 我们要实现的其实是服务端与客户端之间收发消息。 所以 我们就要客户端先发 然后服务端收消息。 然后 服务端收到消息将消息 处理一下 再将消息发回客户端。 所以这个过程中服务端有三个主要的动作 一个是收一个是处理 一个是发。 然后我们的处理怎么处理 我们可以将处理动作暴露出去 交给main.cpp来决定。 ——利用回调函数 main.cpp中将要执行的动作作为函数传给run函数。
如下为接口 //这里的func_t是一个回调函数的类型。 什么类型 使用包装器包装的注意//包含头文件functionalusing func_t functionstring(string); void run(func_t func){isrunning_ true;char inbuffer[1024];while (isrunning_){memset(inbuffer, 0, sizeof(inbuffer));sockaddr_in client;socklen_t client_len;memset(client, 0, sizeof(client));//接收数据的同时监听到客户端的来源ssize_t s recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (sockaddr*)client, client_len);if (s 0) {lg(Waring, recvfrom error, errno: %d, strerror: %s, errno, strerror(errno));continue;}inbuffer[s] 0;//处理数据//创建套接字 用来监听是哪一个客户端string message inbuffer;message func(message);//这里的处理使用一个外部的接口//处理完成后 返回发送给客户端sendto(sockfd_, message.c_str(), message.size(), 0, (sockaddr*)client, sizeof(client));}} UdpServer析构
析构函数不解释 ~UdpServer(){if (sockfd_ 0) close(sockfd_); }
Udpclient 客户端就是给对应的服务端发送数据。 数据被服务端处理后接收即可:
#includeiostream
#includecstdlib
#includeunistd.h
using namespace std;
#includesys/types.h
#includestrings.h
#includesys/socket.h
#includenetinet/in.h
#includearpa/inet.h
#includecstring
#includestring//./udpclient serverip serverport
int main(int argc, char* argv[])
{if (argc ! 3){cout has return endl;return 1;}//string serverip argv[1];uint16_t serverprot stoi(argv[2]);//创建套接字struct sockaddr_in server;bzero(server, sizeof(server));server.sin_family AF_INET;server.sin_port htons(serverprot);server.sin_addr.s_addr inet_addr(serverip.c_str());socklen_t serlen sizeof(server);int sockfd socket(AF_INET, SOCK_DGRAM, 0);if (sockfd 0){cout socker error endl; return 1;}string message;char buffer[1024];while(true){ cout please Enter: endl;getline(cin, message); //发送数据sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)server, serlen);cout yes endl;//接收数据sockaddr_in temp;socklen_t socklen;ssize_t sz recvfrom(sockfd, (void*)buffer, sizeof(buffer) - 1, 0, (sockaddr*)temp, socklen); //sockfd其实就是网卡的pidif(sz 0){buffer[sz] 0;cout buffer endl;}}close(sockfd);return 0;
} 实现客户端之间的聊天功能
Udpserver 实现客户端之间的聊天可是说是在上面的代码中改两个地方。 一个是创建一个哈希表存储有多少客户端连接了服务端。 然后以后发消息就直接便利整个哈希表 然后将数据发给每一个客户端。 如下
#includeiostream
using namespace std;
#includesys/types.h
#includestring
#includesys/socket.h
#includearpa/inet.h
#includestring.h
#includestrings.h
#includeLog.hpp
#includefunctional
#includenetinet/in.h
#includeunordered_mapint defaultport 8080;
string defaultip 0.0.0.0;//包装类要进行修改一下
using func_t functionstring(string, sockaddr_in, unordered_mapstring, sockaddr_in); Log lg;enum
{SockError 2,BindError 3,RecvError 4,
};class UdpServer
{
public:UdpServer(uint16_t port defaultport) : port_(port), ip_(defaultip), isrunning_(false){}//Init不变void Init(){//先创建套接字变量并且完成初始化。 然后就创建网卡文件sockaddr_in local;memset(local, 0, sizeof(local));local.sin_family AF_INET;local.sin_port htons(port_);local.sin_addr.s_addr inet_addr(ip_.c_str());sockfd_ socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ 0){lg(Fatal, create sock error, errno: %d, strerror: %s, errno, strerror(errno));exit(SockError);}lg(Info, create sock success);//绑定if (bind(sockfd_, (sockaddr*)local, sizeof(local)) 0) {lg(Fatal, bind error, errno: %d, strerror: %s, errno, strerror(errno));exit(BindError);}lg(Info, bind success);}//遍历哈希表将数据分发给所有的客户端void BroadCast(string message, unordered_mapstring, sockaddr_in clients){cout 1 endl;for (auto e : clients){sendto(sockfd_, message.c_str(), message.size(), 0, (sockaddr*)e.second, sizeof(e.second));}cout 2 endl;}void run(func_t func){isrunning_ true;char inbuffer[1024];while (isrunning_){memset(inbuffer, 0, sizeof(inbuffer));sockaddr_in client;socklen_t client_len;memset(client, 0, sizeof(client));//接收数据的同时监听到客户端的来源ssize_t s recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (sockaddr*)client, client_len);if (s 0) {lg(Waring, recvfrom error, errno: %d, strerror: %s, errno, strerror(errno));continue;}inbuffer[s] 0;//处理数据//创建套接字 用来监听是哪一个客户端string message inbuffer;message func(message, client, clients);//这里的处理使用一个外部的接口//处理完成后 返回发送给客户端BroadCast(message, clients);}}~UdpServer(){if (sockfd_ 0) close(sockfd_); }
private:int sockfd_;uint16_t port_;string ip_;bool isrunning_;unordered_mapstring, sockaddr_in clients; //添加哈希表}; #includeUdpServer.hpp
#includememory//main.cc主要修改就是Handler函数 我们要通过client里面的ip地址和端口号作为keyclient作为value然后放入哈希表。 同时将message处理一下方便我们观看结果。
string Handler(string message, sockaddr_in client, unordered_mapstring, sockaddr_in clients)
{string tmp inet_ntoa(client.sin_addr) to_string(client.sin_port);if (!clients.count(tmp)){clients[tmp] client; cout ip inet_ntoa(client.sin_addr) : port client.sin_port has add in talk room endl;}message [ string(inet_ntoa(client.sin_addr)) : to_string(client.sin_port) ]#: message; return message;
}// int main(int argc, char* argv[])
{if (argc ! 2){cout has return endl;return 1;}//uint16_t serverport stoi(argv[1]); unique_ptrUdpServer svr(new UdpServer(serverport));//svr-Init();svr-run(Handler);return 0;
}
Udpclient
#includeiostream
using namespace std;
#includestring
#includesys/types.h
#includeLog.hpp
#includesys/socket.h
#includepthread.h
#includearpa/inet.h
#includestring.h
#includenetinet/in.h
Log lg;class ThreadData
{
public:sockaddr_in server;int sockfd;
};//数据接收函数
void* recv_message(void* args)
{char buffer[1024];ThreadData* td static_castThreadData*(args); while (true){//接收数据sockaddr_in temp;socklen_t len;string info;ssize_t s recvfrom(td-sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr*)temp, len);if (s 0){lg(Error, recv error, error: %d, strerror: %s, errno, strerror(errno));continue;}buffer[s] 0;info buffer;cout info endl;}}//数据发送函数
void* send_message(void* args)
{ThreadData* td static_castThreadData*(args);string message;while (true){ getline(cin, message); //获取数据//发送数据sendto(td-sockfd, message.c_str(), message.size(), 0, (sockaddr*)td-server, sizeof(td-server));}
}int main(int argc, char* argv[])
{if (argc ! 3){cout Client server endl;}//先拿到套接字的参数string serverip argv[1];uint16_t serverport stoi(argv[2]);ThreadData td;//创建套接字与打开网卡memset(td.server, 0, sizeof(td.server));td.server.sin_family AF_INET;td.server.sin_port htons(serverport);td.server.sin_addr.s_addr inet_addr(serverip.c_str());td.sockfd socket(AF_INET, SOCK_DGRAM, 0); //创建文件描述符, 网卡的文件描述符 网络传输就是使用网络文件描述符找到对应的文件内的数据if (td.sockfd 0){lg(Error, client create sockfd error, errno: %d, strerror: %s, errno, strerror(errno));exit(1);}//创建线程 然后运行线程 等待线程pthread_t recv, send;pthread_create(recv, nullptr, recv_message, td);pthread_create(send, nullptr, send_message, td);pthread_join(recv, nullptr);pthread_join(send, nullptr);close(td.sockfd);return 0;
}运行结果
最后就是运行结果 运行结果就是下图了 我们已经能够成功的进行两个客户端之间的远程交流 ——————以上就是本节全部内容哦 如果对友友们有帮助的话可以关注博主 方便学习更多知识哦