【计算网络学习笔记】Socket编程UDP实现简单聊天室

🔥个人主页:大白的编程日记
🔥专栏:计算机网络学习笔记

前言
哈喽,各位小伙伴大家好!上期我们讲了网络协议栈 今天我们讲的是Socket编程UDP实现简单聊天室。话不多说,我们进入正题!向大厂冲锋!
2 Socket 编程UDP
UDP 网络编程
简单的回显服务器和客户端代码

V3 版本 - 简单聊天室
UdpServer.hpp
Route.hpp
#pragma once
#include <string>
#include <iostream>
#include <vector>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdint.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include "InetAddr.hpp"
#include"Mutex.hpp"
using namespace MutexModule;
using namespace LogModule;
using namespace std;
class Route
{
public:
Route()
{
}
bool Isexit(InetAddr &peer)
{
for (auto usr : _online_user)
{
// IP和端口号匹配才认为是同一个用户 这样一个IP可以起多个客户端!
if (usr == peer)
{
return 1;
}
}
return 0;
}
//删除一个在线用户
void DeleteUser(InetAddr &peer)
{
auto it = _online_user.begin();
while (it != _online_user.end())
{
if (*it == peer)
{
LOG(LogLevel::INFO) << "删除一个在线用户"<<peer.Message()<<"成功";
_online_user.erase(it);
break;
}
it++;
}
}
//新增一个用户
void AddUser(InetAddr &peer)
{
_online_user.push_back(peer);
}
void MessageRoute(int socket, const string message, InetAddr &peer)
{
//加锁保证多线程执行人任务线程安全
LockGuard lockgurad(_mutex);
if (!Isexit(peer))
{
LOG(LogLevel::INFO) << "新增一个在线用户";
AddUser(peer);
}
string sendstring = "["+peer.Message() + " say:#"+"]" + message;
for (auto peer : _online_user)
{
//给所有用户发送消息
int n = sendto(socket, sendstring.c_str(), sendstring.size(), 0,
(struct sockaddr *)&peer.Addr(), sizeof(peer.Addr()));
}
// 走到这里用户一定存在
if (message == "QUIT")
{
LOG(LogLevel::INFO) << "删除一个在线用户"<<peer.Message();
DeleteUser(peer);
}
}
~Route()
{
}
private:
// 首次发消息等同于登录 记录下Inetaddr;
vector<InetAddr> _online_user; // 在线用户列表
Mutex _mutex;//保证多线程执行人任务访问vector时线程安全
};
UdpClient
#include <iostream>
#include <memory>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdint.h>
#include <arpa/inet.h>
#include"ThreadPool.hpp"
#include "UdpServer.hpp"
using namespace std;
using namespace ThreadPoolModule;
string ip;
uint16_t port;
int sockfd;
pthread_t _tid;
//发送消息模块
void Send()
{
//OS自动bind绑定IP+port
//填写服务器信息
struct sockaddr_in server;
bzero(&server,0);
server.sin_family=AF_INET;
server.sin_port=htons(port);
server.sin_addr.s_addr=inet_addr(ip.c_str());
const string online="inline";
sendto(sockfd,online.c_str(),online.size(),0,
(struct sockaddr*)&server,sizeof(server));
while(1)
{
string input;
cout<<"Please Enter#";
getline(cin,input);
//发消息
int n=sendto(sockfd,input.c_str(),input.size(),0,
(struct sockaddr*)&server,sizeof(server));
if(input=="QUIT")
{
sleep(1);
pthread_cancel(_tid);
break;
}
}
}
//接受消息模块
void Receive()
{
while(1)
{
//收消息
char buff[1024];
struct sockaddr t;
socklen_t len;
//今天这里不接受服务器套接字信息因为只有一个服务器 但是服务端可能有多个
int m=recvfrom(sockfd,buff,sizeof(buff)-1,0,(struct sockaddr*)&t,&len);
if(m>0)
{
buff[m]=0;
cerr<<buff<<endl;
}
}
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
cout << "Usage:" << argv[0] << "ip port" << endl;
return 1;
}
ip=argv[1];
port=stoi(argv[2]);
//创建套接字
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
cerr<<"create socket error"<<endl;
}
//一个线程发送消息 一个线程接受消息
Thread recver(Receive);
Thread sender(Send);
recver.Start();
sender.Start();
_tid=recver.Id();
recver.Join();
sender.Join();
return 0;
}
UdpServer
#pragma once
#include <iostream>
#include <sys/types.h>
#include <string>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <functional>
#include "Log.hpp"
#include "Mutex.hpp"
#include"InetAddr.hpp"
using namespace std;
using namespace LogModule;
const int defaultfd = -1;
using fun_t = function<void(int,const string &, InetAddr&)>;
class UdpServer
{
public:
UdpServer(const uint16_t port, fun_t fun)
: _sockfd(defaultfd),
// _ip(ip),
_port(port),
_isrunning(0),
_func(fun)
{
}
void Init()
{
// 创建套接字
_sockfd = _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (_sockfd < 0)
{
LOG(LogLevel::FATAL) << "socket create error";
exit(1);
}
LOG(LogLevel::INFO) << "socket success sockfd: " << _sockfd;
// 绑socket信息 ip和端口号
// 填充socket_in结构体
struct sockaddr_in local;
bzero(&local, sizeof(local));
local.sin_family = AF_INET;
// IP的端口号的信息要发送到网络
// 所以本地格式-》网络序列
local.sin_port = htons(_port);
// IP也是如此,1.IP转成4字节 2.4字节转成网络序列 ->
// in_addr_t inet_addr(const char *cp);
// local.sin_addr.s_addr = inet_addr(_ip.c_str());
local.sin_addr.s_addr = INADDR_ANY;
// 绑定端口号和IP地址
int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
if (n < 0)
{
LOG(LogLevel::FATAL) << "bind error";
exit(2);
}
LOG(LogLevel::INFO) << "bind success sockfd: " << _sockfd;
}
void Start()
{
_isrunning = 1;
while (_isrunning)
{
// 收消息
char buffer[1024];
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
// 接受客户端消息
ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1,
0, (struct sockaddr *)&peer, &len);
if (s > 0)
{
InetAddr client(peer);
// 网络序转专门主机
int port = ntohs(peer.sin_port);
// 4字节IP-》点分十进制IP地址
string ip = inet_ntoa(peer.sin_addr);
buffer[s] = 0;
// LOG(LogLevel::INFO) << "[" << ip << ":"
// << port << "]" << buffer << buffer;
_func(_sockfd,buffer,client); // 回调处理
// 发消息
// string echo_string = "server say#";
// echo_string += buffer;
// 给客户端直接发送消息 不面向链接
// sendto(_sockfd, result.c_str(), result.size(),
// 0, (struct sockaddr *)&peer, len);
}
}
}
~UdpServer()
{
}
private:
int _sockfd = -1;
uint16_t _port;
// string _ip;
fun_t _func; // 服务器的回调函数
bool _isrunning;
};


后言
这就是Socket编程UDP实现简单聊天室。大家自己好好消化!今天就分享到这! 感谢各位的耐心垂阅!咱们下期见!拜拜~


3883

被折叠的 条评论
为什么被折叠?



