网络学了点socket,写个聊天室,还得改进

目录

第一版:

common

服务端:

客户端

第一版问题总结:

第二版

服务端:

客户端:

改进:

Windows客户端

一些小问题

还可以进行的改进


这篇文章我就先不讲网络基础的东西了,我讲讲在我进行制作我这个拉跨聊天室中遇到的问题,并写了三版代码.

第一版:

common

#pragma once
#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "LockGuard.hpp"
enum
{SOCKET_ERROR = 1,BIND_ERROR,USAGE_ERROR
};void Setserver(struct sockaddr_in &server,std::string &serverip,uint16_t &serverport)
{bzero(&server, sizeof(server));server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());server.sin_family=AF_INET;
}class InetAddr
{void GetAddress(uint16_t *Port, std::string *Ip){*Port = ntohs(_Addr.sin_port);*Ip = inet_ntoa(_Addr.sin_addr);}public:InetAddr(struct sockaddr_in &Addr): _Addr(Addr){GetAddress(&_Port, &_Ip);}uint16_t Port(){return _Port;}std::string Ip(){return _Ip;}private:struct sockaddr_in _Addr;uint16_t _Port;std::string _Ip;
};

服务端:

#pragma once
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
#include "Common.hpp"
const static int defaultfd = -1;class Udpserver
{public:Udpserver(uint16_t port): _socketfd(defaultfd), _prot(port), _isrunning(false){}void InitServer(){_socketfd = socket(AF_INET, SOCK_DGRAM, 0);if (_socketfd < 0){LOG(WARNING, "%s", "sockfd创建失败");}LOG(INFO, "%s", "sock创建成功");// 填充sockaddr_in结构struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;    // 设置家族协议local.sin_port = htons(_prot); // 设置端口号 换成网络序列// port要经过网络传输给对面,先到网络,_port:主机序列-> 主机序列,转成网络序列// a. 字符串风格的点分十进制的IP地址转成 4 字节IP// b. 主机序列,转成网络序列// in_addr_t inet_addr(const char *cp) -> 同时完成 a & b// local.sin_addr.s_addr = inet_addr(_ip.c_str()); // "192.168.3.1" -> 字符串风格的点分十进制的IP地址 -> 4字节IPlocal.sin_addr.s_addr = INADDR_ANY; // 随机ip地址 一般不能绑定确定ip地址// 开始绑定int n = bind(_socketfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(FATAL, "%s", "bind error");exit(BIND_ERROR);}LOG(INFO, "%s", "bind success");}void Start(){_isrunning = true;while (_isrunning){char Inbuffer[1024];struct sockaddr_in peer;socklen_t socklen = sizeof(peer); // 初始化为sock// 让server收取数据 获取客户端socketssize_t n = recvfrom(_socketfd, Inbuffer, sizeof(Inbuffer), 0, (struct sockaddr *)&peer,&socklen); // 接受if (n > 0){std::cout <<"client say:";std::cout << Inbuffer << std::endl; // 成功就打印出来// sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);LOG(DEBUG, "建立 客户IP:%s 连接端口:%s", netAddr.Ip().c_str(), netAddr.Port());// sendto(_socketfd,buffer,sizeof(buffer),0,(struct sockaddr*)&sockaddr_in,socklen);//发送}/////发///InetAddr netAddr(peer);std::string message;std::cout << "server:";getline(std::cin, message);sendto(_socketfd, message.c_str(), message.size(), 0, (struct sockaddr *)&peer, socklen); // 发送}_isrunning = false;}private:int _socketfd;uint16_t _prot;bool _isrunning;
};

客户端

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
#include "Common.hpp"static bool isrunning = false;
static std::string Clientname;
void useagge(std::string proc)
{std::cout << "Usage:\n\t" << proc << " serverip serverport\n"<< std::endl;
}int main(int argc, char *argv[])
{if (argc != 3){useagge(argv[0]);exit(USAGE_ERROR);}std::string serverip = argv[1];uint16_t serverport = std::atoi(argv[2]);std::cout << "你是?" << std::endl;std::cin >> Clientname;// 创建socket;int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){LOG(WARNING, "%s", "Sock错误创建失败");}LOG(INFO, "%s", "sock创建成功");isrunning = true;struct sockaddr_in server;Setserver(server, serverip, serverport);struct sockaddr_in local;bzero(&local, sizeof(local));std::cout << "可以进行通信了!" << std::endl;while (isrunning){std::cout << "server:";std::string message;getline(std::cin, message);sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, socklen); // 发送struct sockaddr_in peer;socklen_t socklen = sizeof(peer); // 初始化为sockchar buffer[1024];ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &socklen);if (n > 0){buffer[n] = 0;std::cout << "server: " << buffer << std::endl;}}return 0;
}

第一版问题总结:

我一开始是想着这个流程,因为一开始服务端只是接受客户端,服务端不会发消息给客户端,所以我想在原基础上,让两端都可以接受和发送,当时就有想可以多线程实行接受和发的任务,但是觉得上线程太麻烦就决定是服务端发->客户端收->客户端发->服务器收,这一条链路实行,但是问题是,我把收发是写在循环里,而 recvfrom是非阻塞等待的,所以双方实际上永远等不到对方信息

所以实际上仍然是要让多线程实行接受和发的任务

第二版

服务端:

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <thread>
#include <mutex>
#include "log.hpp"
#include "Common.hpp"
const static int defaultfd = -1;public:Udpserver(uint16_t port): _socketfd(defaultfd), _prot(port), _isrunning(false){_socklen = sizeof(_peer);InitServer();}void InitServer(){_socketfd = socket(AF_INET, SOCK_DGRAM, 0);if (_socketfd < 0){LOG(WARNING, "%s", "sockfd创建失败");}LOG(INFO, "%s", "sock创建成功");// 填充sockaddr_in结构struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;    // 设置家族协议local.sin_port = htons(_prot); // 设置端口号 换成网络序列// port要经过网络传输给对面,先到网络,_port:主机序列-> 主机序列,转成网络序列// a. 字符串风格的点分十进制的IP地址转成 4 字节IP// b. 主机序列,转成网络序列// in_addr_t inet_addr(const char *cp) -> 同时完成 a & b// local.sin_addr.s_addr = inet_addr(_ip.c_str()); // "192.168.3.1" -> 字符串风格的点分十进制的IP地址 -> 4字节IPlocal.sin_addr.s_addr = INADDR_ANY; // 随机ip地址 一般不能绑定确定ip地址// 开始绑定int n = bind(_socketfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(FATAL, "%s", "bind error");exit(BIND_ERROR);}LOG(INFO, "%s", "bind success");_isrunning = true;}void Start()void receive(){_isrunning = true;while (_isrunning){char Inbuffer[1024] = {0}; // 初始化缓冲区struct sockaddr_in tempPeer;socklen_t tempSocklen = sizeof(tempPeer);ssize_t n = recvfrom(_socketfd, Inbuffer, sizeof(Inbuffer) - 1, 0, (struct sockaddr *)&tempPeer, &tempSocklen);if (n > 0){std::cout <<"client say:";std::cout << Inbuffer << std::endl; // 成功就打印出来// sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);LOG(DEBUG, "建立 客户IP:%s 连接端口:%s", netAddr.Ip().c_str(), netAddr.Port());// sendto(_socketfd,buffer,sizeof(buffer),0,(struct sockaddr*)&sockaddr_in,socklen);//发送Inbuffer[n] = 0;std::lock_guard<std::mutex> lock(_peerMutex);_peer = tempPeer;_socklen = tempSocklen;std::cout << "client says:" << Inbuffer << std::endl;}/////发///InetAddr netAddr(peer);else{perror("recvfrom error");}}}void sent(){while (_isrunning){std::string message;std::cout << "server: ";std::cin >> message;std::lock_guard<std::mutex> lock(_peerMutex);ssize_t sent = sendto(_socketfd, message.c_str(), message.size(), 0, (struct sockaddr *)&_peer, _socklen);if (sent == -1){perror("sendto error");}}_isrunning = false;}void Start(){std::thread recvThread(&Udpserver::receive, this);std::thread sendThread(&Udpserver::sent, this);recvThread.detach();sendThread.detach();while (_isrunning){std::this_thread::sleep_for(std::chrono::seconds(1));}close(_socketfd);}private:int _socketfd;uint16_t _prot;bool _isrunning;//struct sockaddr_in _peer;socklen_t _socklen;std::mutex _peerMutex;
};

客户端:

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <memory>
#include <thread>
#include "log.hpp"
#include "Common.hpp"static bool isrunning = false;
static std::string Clientname;class Client
{
public:Client(const std::string &server_ip, uint16_t server_port){_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == -1){perror("socket creation failed");exit(EXIT_FAILURE);}memset(&_serverAddr, 0, sizeof(_serverAddr));_serverAddr.sin_family = AF_INET;_serverAddr.sin_port = htons(server_port);if (inet_pton(AF_INET, server_ip.c_str(), &_serverAddr.sin_addr) <= 0){perror("inet_pton failed");close(_sockfd);exit(EXIT_FAILURE);}_isrunning = true;}void start(){std::cout << "你是? ";std::cin >> _clientName;std::cout << "可以进行通信了!" << std::endl;std::thread recvThread(&Client::receive, this);std::thread sendThread(&Client::send, this);recvThread.detach();sendThread.detach();while (_isrunning){std::this_thread::sleep_for(std::chrono::seconds(1));}close(_sockfd);}private:void receive(){while (_isrunning){char buffer[1024] = {0};struct sockaddr_in peer;socklen_t socklen = sizeof(peer);ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &socklen);if (n > 0){buffer[n] = 0;std::cout << "server: " << buffer << std::endl;}else if (n == -1){perror("recvfrom error");}}}void send(){while (_isrunning){std::cout << _clientName << ": ";std::string message;std::cin >> message;ssize_t sent = sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&_serverAddr, sizeof(_serverAddr));if (sent == -1){perror("sendto error");}}}private:int _sockfd;struct sockaddr_in _serverAddr;bool _isrunning;std::string _clientName;
};

改进:

这一版上,我添加了多线程和锁,能让客户端服务端进行并发的运行,并收发消息

Windows客户端

由于我想让Windows朋友也能与我建立通信,所以我在客户端上进行了修改成Windows版本

 #define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <thread>
#include <memory>#pragma comment(lib, "Ws2_32.lib")static bool isrunning = false;
static std::string Clientname;class Client
{
public:Client(const std::string& server_ip, uint16_t server_port){WSADATA wsaData;int result = WSAStartup(MAKEWORD(2, 2), &wsaData);if (result != 0){std::cerr << "WSAStartup failed: " << result << std::endl;exit(EXIT_FAILURE);}_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == INVALID_SOCKET){std::cerr << "socket creation failed: " << WSAGetLastError() << std::endl;WSACleanup();exit(EXIT_FAILURE);}memset(&_serverAddr, 0, sizeof(_serverAddr));_serverAddr.sin_family = AF_INET;_serverAddr.sin_port = htons(server_port);if (inet_pton(AF_INET, server_ip.c_str(), &_serverAddr.sin_addr) <= 0){std::cerr << "inet_pton failed: " << WSAGetLastError() << std::endl;closesocket(_sockfd);WSACleanup();exit(EXIT_FAILURE);}_isrunning = true;}void start(){std::cout << "你是? ";std::cin >> _clientName;std::cout << "可以进行通信了!" << std::endl;std::thread recvThread(&Client::receive, this);std::thread sendThread(&Client::send, this);recvThread.detach();sendThread.detach();while (_isrunning){std::this_thread::sleep_for(std::chrono::seconds(1));}closesocket(_sockfd);WSACleanup();}private:void receive(){while (_isrunning){char buffer[1024] = { 0 };struct sockaddr_in peer;int peerlen = sizeof(peer);int n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &peerlen);if (n > 0){buffer[n] = 0;std::cout << "server: " << buffer << std::endl;}else if (n == SOCKET_ERROR){std::cerr << "recvfrom error: " << WSAGetLastError() << std::endl;}}}void send(){while (_isrunning){std::cout << _clientName << ": ";std::string message;std::cin >> message;int sent = sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&_serverAddr, sizeof(_serverAddr));if (sent == SOCKET_ERROR){std::cerr << "sendto error: " << WSAGetLastError() << std::endl;}}}private:SOCKET _sockfd;struct sockaddr_in _serverAddr;bool _isrunning;std::string _clientName;
};void useagge(const std::string& proc)
{std::cout << "Usage:\n\t" << proc << " serverip serverport\n"<< std::endl;
}int main()
{std::string serverip;std::string portStr;std::cout << "输入服务器ip: ";std::cin >> serverip;std::cout << "输入服务器端口号: ";std::cin >> portStr;uint16_t serverport;try{serverport = static_cast<uint16_t>(std::stoi(portStr));}catch (const std::exception& e){std::cerr << "无效的端口号: " << e.what() << std::endl;return EXIT_FAILURE;}std::unique_ptr<Client> csvr = std::make_unique<Client>(serverip, serverport); // C++14csvr->start();return 0;
}

一些小问题

由于我用的云服务器,大部分端口号是默认禁用的,所以端口号要自己进行开放,我用的阿里云,开放端口的地方在这里

还可以进行的改进

1.目前服务端和客户端仍然是1对1的关系,如果有第二个用户上线,就会挤占第一个用户,所以这里可以用一个vector来对用户的ip进行管理,来统一收所有用户消息

2.目前还没有将用户的名字传输给服务端,后续可以加上

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/25010.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot-集成TOTP

TOTP验证码提供了一种高效且安全的身份验证方法。它不仅减少了依赖短信或其他通信方式带来的成本和延时&#xff0c;还通过不断变换的密码增加了破解的难度。未来&#xff0c;随着技术的进步和对安全性要求的提高&#xff0c;TOTP及其衍生技术将继续发展并被更广泛地应用。TOTP…

多模态模型是什么意思(国内外的AI多模态有哪些)

在人工智能和机器学习的领域&#xff0c;我们经常会遇到一些专业术语&#xff0c;这些术语可能会让初学者感到困惑。其中&#xff0c;"多模态模型"就是这样一个概念。 什么是AI多模态。它是什么意思呢&#xff1f; 那么&#xff0c;多模态模型是什么意思呢&#xff1…

【Python】数据处理:SQLite操作

使用 Python 与 SQLite 进行交互非常方便。SQLite 是一个轻量级的关系数据库&#xff0c;Python 标准库中包含一个名为 sqlite3 的模块&#xff0c;可以直接使用。 import sqlite3数据库连接和管理 连接到 SQLite 数据库。如果数据库文件不存在&#xff0c;则创建一个新数据库…

SystemVerilog Interface Class的妙用

前言 Interface Class是在SystemVerilog 2012版本中引入的&#xff0c;但目前在验证中几乎很少采用&#xff0c;大多数验证工程师要么不知道它&#xff0c;要么没有看到使用它的任何好处&#xff0c;这使得Interface Class成为一个未被充分使用和不被重视的特性。本文将举两个…

docker镜像深入理解

大家好&#xff0c;本篇文章和大家聊下docker相关的话题~~ 工作中经常有关于docker镜像的问题&#xff0c;让人百思不解 docker镜像加载到系统中到哪里去了&#xff1f;docker load 加载镜像的流程是怎样的&#xff1f;为什么容器修改内容后&#xff0c;删除容器后再次开启容…

阿里云 MQTT 服务器搭建与测试(上传和下发数据finish)

一、 MQTT 概念 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于 TCP/IP协议上,由 IBM 在 1999 年发布。MQTT 最大优点在于,可以以极少的代码和有限的带宽,…

c++之旅第十弹——IO流

大家好啊&#xff0c;这里是c之旅第十弹&#xff0c;跟随我的步伐来开始这一篇的学习吧&#xff01; 如果有知识性错误&#xff0c;欢迎各位指正&#xff01;&#xff01;一起加油&#xff01;&#xff01; 创作不易&#xff0c;希望大家多多支持哦&#xff01; 一.流的概念&…

kNN算法-概述

所谓kNN算法就是K-nearest neigbor algorithm。这是似乎是最简单的监督机器学习算法。在训练阶段&#xff0c;kNN算法存储了标签训练样本数据。简单地说&#xff0c;就是调用训练方法时传递给它的标签训练样本会被它存储起来。 kNN算法也叫lazy learning algorithm懒惰学习算法…

计算机网络 期末复习(谢希仁版本)第8章

元文件就是一种非常小的文件&#xff0c;它描述或指明其他文件的一些重要信息。这里的元文件保存了有关这个音频/视频文件的信息。 10. 流式&#xff1a;TCP&#xff1b;流式实况&#xff1a;UDP。

Huawei 大型 WLAN 组网 AC 间漫游

AC1配置命令 <AC6005>display current-configuration # vlan batch 100 # interface Vlanif100description to_S3_CAPWAPip address 10.0.100.254 255.255.255.0 # interface GigabitEthernet0/0/1port link-type trunkport trunk allow-pass vlan 100# ip route-stati…

Chrome浏览器书签同步不及时怎么办?两种方法帮你解决!

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

7种方法教你如何解决msvcp140_1.dll丢失问题,一键修复dll丢失问题

msvcp140_1.dll 是 Microsoft Visual C 2015 Redistributable 的一部分&#xff0c;它提供了运行时所需的 C 标准库的实现。这个 DLL 文件对于依赖 Visual C 2015 编译的应用程序至关重要&#xff0c;因为它包含了程序运行时所必需的函数和资源。 作用 运行时支持&#xff1a…

28-LINUX--I/O复用-epoll

一.epoll概述 epoll 是 Linux 特有的 I/O 复用函数。它在实现和使用上与 select、poll 有很大差异。首 先&#xff0c;epoll 使用一组函数来完成任务&#xff0c;而不是单个函数。其次&#xff0c;epoll 把用户关心的文件描述 符上的事件放在内核里的一个事件表中。从而无需像…

mysql (事物)

一.什么是事物 事物是一组操作的集合&#xff0c;不可分割的工作单位&#xff0c;事物会把所有的操作当作一个整体一起向系统提交或撤销操作请求&#xff0c;就是这些操作要么一起成功要么一起失败。 二.事物操作 &#xff08;这个就是一个理解&#xff09; 1.事务特性 原子性…

超详解——python数字和运算——小白篇

目录 1.位运算 2. 常用内置函数/模块 math模块&#xff1a; random模块&#xff1a; decimal模块&#xff1a; 3.内置函数&#xff1a; 总结&#xff1a; 1.位运算 位运算是对整数在内存中的二进制表示进行操作。Python支持以下常见的位运算符&#xff1a; 按位与&…

C语言王国——数据的内存管理

目录 一、引言 二、整形在内存中的存储 2.1 进制之间的转换 2.1.1 整形的二进制 2.1.2 十进制和二进制 2.1.3 十进制和八进制的转换 2.1.4 十六进制和十进制的转换 2.2 原码&#xff0c;反码&#xff0c;和补码 三、大、小端字节序 3.1 大小端的定义 3.2 为什么会有大…

pxe批量部署linux介绍

1、PXE批量部署的作用及必要性&#xff1a; 1&#xff09;智能实现操作系统的批量安装&#xff08;无人值守安装&#xff09;2&#xff09;减少管理员工作&#xff0c;提高工作效率3&#xff09;可以定制操作系统的安装流程a.标准流程定制(ks.cfg)b.自定义流程定制(ks.cfg(%pos…

LLVM Cpu0 新后端8 尾调用优化 Stack Overflow Exception异常

想好好熟悉一下llvm开发一个新后端都要干什么&#xff0c;于是参考了老师的系列文章&#xff1a; LLVM 后端实践笔记 代码在这里&#xff08;还没来得及准备&#xff0c;先用网盘暂存一下&#xff09;&#xff1a; 链接: https://pan.baidu.com/s/1V_tZkt9uvxo5bnUufhMQ_Q?…

【iOS】JSONModel源码阅读笔记

文章目录 前言一、JSONModel使用二、JSONModel其他方法转换属性名称 三、源码分析- (instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err[self init]__setup____inspectProperties - (BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMa…

android集成百度文心一言实现对话功能,实战项目讲解,人人都能拥有一款ai应用

大家好&#xff0c;今天给大家讲解下如何实现一个基于百度文心一言的app功能&#xff0c;app内部同时集成了讯飞的语音识别。本文适用于有android基础的小伙伴阅读&#xff0c;文章末尾放上本项目用到的全部实例代码&#xff0c;在使用前请务必看完本文章。 先来给大家看看效果…