Linux使用c++我们会使g++来编码,使用gdb工具进行代码调试。同时在大型项目中,我们编写makefile来自动化编译。

知识点

  • 使用 g++ 编译器编译代码
  • 使用 gdb 进行调试
  • 编写 makefile

首先明确c++编译过程分为四个阶段:预处理 编译 汇编 链接

预处理负责 宏定义的替换、条件编译、将include头文件展开到正文。

编译负责将源码转换成汇编代码。

汇编负责将汇编代码转成可重定位的目标二进制文件。

链接 负责将所有的目标文件连接起来,进行符号解析和重定位,最后生成可执行文件。

g++ test.cpp

安装 gdb

1.

wget http://ftp.gnu.org/gnu/gdb/gdb-8.0.1.tar.gztar -zxvf gdb-8.0.1.tar.gzcd gdb-8.0.1/ ./configuremakesudo make installgdb -v

2.

sudo apt install gdb

编译时要加入-g 参数

为了能使用 gdb 调试,我们在用 g++ 编译时要将 -g 参数加上,如:

g++ -g -o test a.cpp b.cpp

gdb 常用调试命令

以前面“多文件编译”的程序为例,我们可使用如下命令进入 gdb 调试:

gdb test
常用的调试命令有:l:查看代码
b 5:在程序的第 5 行添加断点
info break:查看断点
r:开始运行
s:进入函数内部
n:进入下一步
finish:跳出函数内部
c:运行到下一个断点

编写 makefile 进行自动编译

在大型项目中有大量的源代码文件,我们不可能每次都逐个敲 g++ 命令来进行编译,而是采用编写 makefile 的方式来进行自动编译,提高效率。

和创建源代码文件一样,我们可以直接用 vi 编辑器来创建 makefile,如下:

vi makefile

makefile 的一般格式如下:

目标名1:依赖文件1,依赖文件2,依赖文件3g++ 编译命令
目标名2:依赖文件4,依赖文件5g++ 编译命令

其中目标名可以由自己定义,也可以是一个文件的名字;依赖文件就是说要达成这个目标所需要的文件。 仍以前面的“多文件编译”代码为例,我们可以写出如下的 makefile 文件(注意:makefile 文件中要使用 tab 键,不能使用空格键,否则会报错):

target:a.cpp b.og++ -o test a.cpp b.o
b.o:b.cppg++ -c b.cpp

可以看出,target 依赖于 a.cpp 和 b.o,而 b.o 依赖于 b.cpp,因此编译时发现 b.cpp 更新了的话就会先执行后面的命令来更新 b.o

保存好 makefile 文件之后,我们用命令行输入 make 即可进行自动编译:

快速清理目标文件

有时候我们想要删掉 makefile 产生出来的所有目标文件,如果逐个去删显得过于麻烦,因此我们可以借助 make clean。

仍然是在前面的 makefile 文件中修改,在后面补上一个 clean:,以及相应的清除命令:

target:a.cpp b.og++ -o test a.cpp b.o
b.o:b.cppg++ -c b.cpp
.PHONY:clean
clean:rm *.orm test

这样当我们在命令行执行 make clean 就可删掉所有目标文件。

服务器和客户端进行简单的 TCP 通信

在 TCP/IP 四层参考模型中,从上往下有四种层次:应用层、传输层、网络层、网络接口层。TCP 是传输层中重要的一项协议,也是我们常用的进程间通信方式,因此掌握 C++ 中的 TCP 套接字网络编程十分重要。

知识点

  • 计算机网络传输层基础知识
  • TCP 套接字网络编程

传输层的作用

传输层的根本目的是在网络层提供的数据通信服务基础上,实现主机的进程间通信的可靠服务。

有以下两个要点:为位于两个主机内部的两个应用进程之间提供通信服务、提供可靠的通信服务。

套接字

“套接字”表示一个 IP 地址与对应的一个端口号。例如,一个 IP 地址为 172.31.75.8 的客户端使用 8050 端口号,那么标识客户端的套接字为“172.31.75.8:8050”。

端口号

端口号为 0-65535 之间的整数,有 3 种类型:熟知端口号、注册端口号、临时端口号。

熟知端口号:给每种服务器分配确定的全局端口号。每个用户进程都知道相应的服务器进程的熟知端口号,范围为 0-1023,它是统一分配和控制的。

注册端口号:在 IANA 注册的端口号,数值范围为 1024-49151。

临时端口号:客户端程序使用临时端口号,它是运行在客户端上的 TCP/IP 软件随机选取的,范围为 49152-65535。

我们平时进行网络编程时服务器最好使用注册端口号,而客户端的端口号则是系统随机分配的,即临时端口号。

UDP 用户数据报协议

UDP 协议主要有以下一些特点:

  • 无连接的:发送数据之前不需要建立连接,因此减少了开销和发送数据之前的时延。
  • 尽最大努力交付:即不保证可靠交付,因此主机不需要维持复杂的连接状态表。
  • 面向报文的:UDP 对应用层传递下来的报文,既不合并,也不拆分,而是保留这些报文的边界。UDP 对于应用程序提交的报文,添加头部后就向下提交给网络层。
  • 没有拥塞控制:网络出现的拥塞时,UDP 不会使源主机的发送速率降低。这对某些实时应用是很重要的,很适合多媒体通信的要求。
  • 支持多对多的交互通信

UDP 的适用场景:

  • 适用于少量(几百个字节)的数据。
  • 对性能的要求高于对数据完整性的要求,如视频播放、P2P、DNS 等。
  • 需要“简短快捷”的数据交换 简单的请求与应答报文交互,如在线游戏。
  • 需要多播和广播的应用,源主机以恒定速率发送报文,拥塞发生时允许丢弃部分报文,如本地广播、隧道 VPN。

TCP 传输控制协议

用 C++ 进行 TCP 套接字网络编程

网络编程中我们一般会使用 C/S 架构,即包含服务器端和客户端。

TCP 网络编程的服务器端

服务器端一般先用 socket 创建一个套接字,然后用 bind 给这个套接字绑定地址(即 ip+端口号),然后调用 listen 把这个套接字置为监听状态,随后调用 accept 函数从已完成连接队列中取出成功建立连接的套接字,以后就在这个新的套接字上调用 send、recv 来发送数据、接收数据,最后调用 close 来断开连接释放资源即可。

过程如下:

TCP 网络编程的客户端

与服务器不同,客户端并不需要 bind 绑定地址,因为端口号是系统自动分配的,而且客户端也不需要设置监听的套接字,因此也不需要 listen。客户端在用 socket 创建套接字后直接调用 connect 向服务器发起连接即可,connect 函数通知 Linux 内核完成 TCP 三次握手连接,最后把连接的结果作为返回值。成功建立连接后我们就可以调用 send 和 recv 来发送数据、接收数据,最后调用 close 来断开连接释放资源。

完整流程

TCP 网络编程的整体流程如下图所示:

TCP 网络编程的相关数据结构

地址结构:

struct sockaddr_in {short int sin_family; /* 地址族 */unsigned short int sin_port; /* 端口号 */struct in_addr sin_addr; /* ip地址 */unsigned char sin_zero[8];
};

上述结构体涉及到的另一个结构体 in_addr 如下:

struct in_addr {unsigned long s_addr;
};

TCP 网络编程各函数的定义

int socket( int domain, int type,int protocol)
/*
功能:创建一个新的套接字,返回套接字描述符
参数说明:
domain:域类型,指明使用的协议栈,如TCP/IP使用的是PF_INET,其他还有AF_INET6、AF_UNIX
type:指明需要的服务类型, 如
SOCK_DGRAM:数据报服务,UDP协议
SOCK_STREAM:流服务,TCP协议
protocol:一般都取0(由系统根据服务类型选择默认的协议)
*/
int bind(int sockfd,struct sockaddr* my_addr,int addrlen)
/*
功能:为套接字绑定地址
TCP/IP协议使用sockaddr_in结构,包含IP地址和端口号,服务器使用它来指明熟知的端口号,然后等待连接
参数说明:
sockfd:套接字描述符,指明创建连接的套接字
my_addr:本地地址,IP地址和端口号
addrlen:地址长度
*/
int listen(int sockfd,int backlog)
/*
功能:
将一个套接字置为监听模式,准备接收传入连接。用于服务器,指明某个套接字连接是被动的监听状态。
参数说明:
Sockfd:套接字描述符,指明创建连接的套接字
backlog: linux内核2.2之前,backlog参数=半连接队列长度+已连接队列长度;linux内核2.2之后,backlog参数=已连接队列(Accept队列)长度
*/
int accept(int sockfd, structsockaddr *addr, int *addrlen)
/*
功能:从已完成连接队列中取出成功建立连接的套接字,返回成功连接的套接字描述符。
参数说明:
Sockfd:套接字描述符,指明正在监听的套接字
addr:提出连接请求的主机地址
addrlen:地址长度
*/
int send(int sockfd, const void * data, int data_len, unsigned int flags)
/*
功能:在TCP连接上发送数据,返回成功传送数据的长度,出错时返回-1。send会将数据移到发送缓冲区中。
参数说明:
sockfd:套接字描述符
data:指向要发送数据的指针
data_len:数据长度
flags:通常为0
*/
int recv(int sockfd, void *buf, intbuf_len,unsigned int flags)
/*
功能:接收数据,返回实际接收的数据长度,出错时返回-1。
参数说明:
Sockfd:套接字描述符
Buf:指向内存块的指针
Buf_len:内存块大小,以字节为单位
flags:一般为0
*/
close(int sockfd)
/*
功能:撤销套接字。如果只有一个进程使用,立即终止连接并撤销该套接字,如果多个进程共享该套接字,将引用数减一,如果引用数降到零,则关闭连接并撤销套接字。
参数说明:
sockfd:套接字描述符
*/
int connect(int sockfd,structsockaddr *server_addr,int sockaddr_len)
/*
功能: 同远程服务器建立主动连接,成功时返回0,若连接失败返回-1。
参数说明:
Sockfd:套接字描述符,指明创建连接的套接字
Server_addr:指明远程端点:IP地址和端口号
sockaddr_len :地址长度
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <iostream>
using namespace std;int main()
{//定义sockfdint server_sockfd = socket(AF_INET,SOCK_STREAM, 0);//定义sockaddr_instruct sockaddr_in server_sockaddr;server_sockaddr.sin_family = AF_INET;//TCP/IP协议族server_sockaddr.sin_port = htons(8023);//端口号server_sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//ip地址,127.0.0.1是环回地址,相当于本机ip//bind,成功返回0,出错返回-1if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1){perror("bind");//输出错误原因exit(1);//结束程序}//listen,成功返回0,出错返回-1if(listen(server_sockfd,20) == -1){perror("listen");//输出错误原因exit(1);//结束程序}//客户端套接字struct sockaddr_in client_addr;socklen_t length = sizeof(client_addr);//成功返回非负描述字,出错返回-1int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);if(conn<0){perror("connect");//输出错误原因exit(1);//结束程序}cout<<"客户端成功连接\n";//接收缓冲区char buffer[1000];//不断接收数据while(1){memset(buffer,0,sizeof(buffer));int len = recv(conn, buffer, sizeof(buffer),0);//客户端发送exit或者异常结束时,退出if(strcmp(buffer,"exit")==0 || len<=0)break;cout<<"收到客户端信息:"<<buffer<<endl;}close(conn);close(server_sockfd);return 0;
}
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <iostream>
using namespace std;int main()
{//定义sockfdint sock_cli = socket(AF_INET,SOCK_STREAM, 0);//定义sockaddr_instruct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;//TCP/IP协议族servaddr.sin_port = htons(8023);  //服务器端口servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //服务器ip//连接服务器,成功返回0,错误返回-1if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){perror("connect");exit(1);}cout<<"连接服务器成功!\n";char sendbuf[100];char recvbuf[100];while (1){memset(sendbuf, 0, sizeof(sendbuf));cin>>sendbuf;send(sock_cli, sendbuf, strlen(sendbuf),0); //发送if(strcmp(sendbuf,"exit")==0)break;}close(sock_cli);return 0;
}

多线程客户端

我们前面几次实验所使用的客户端都是单线程的,只具有发送功能,但一个具备聊天功能的客户端应该同时具备发送和接收信息的功能,因此我们需要使用多线程来实现客户端,一个线程可以接收消息并打印,另一个线程可以输入信息并发送

知识点

  • 多线程客户端设计
  • TCP 套接字网络编程
  • C++ 11 的 thread 库使用
  • 面向对象程序设计思想

具体要求

把前面实验的客户端升级为多线程客户端(一个线程用于接收并打印信息、一个线程用于输入并发送信息),为前面实验的多线程服务器添加自动回复客户端的代码,用一个终端运行服务器程序,多个终端运行客户端程序,多个客户端都能发送信息送达服务器并收到服务器的应答,并将应答打印到客户端终端上,当用户在客户端输入 exit 时,要结束两个线程之后再结束客户端进程。

  1. 编写一个客户端类 client ,有发送线程和接收线程,可以同时发送消息和接收消息。
  2. 要编写多个源代码文件:client 头文件给出 client 类声明、client.cpp 给出类方法具体实现、test_client.cpp 中编写主函数创建 client 实例对象并测试。
  3. 当用户在客户端输入 exit 时,要结束发送线程和接收线程之后才退出主线程。
  4. 服务器程序要在实验 3 的基础上进行一定修改,能够回复消息。
  5. 编写 Makefile 进行自动编译,使用 git 管理版本。

客户端应先 connect 服务器建立连接,成功连接之后就创建发送线程和接收线程,与服务器类的设计同理,我们需要将发送线程和接收线程的函数设为静态成员函数,发送线程和接收线程中都使用 while(1) 的循环结构,循环终止的条件是用户输入了 exit 或者对端关闭了连接。

添加用户注册功能

在前面的实验中,我们已经将服务器和客户端的多线程框架建立好了,接下来可以开始实现具体的业务逻辑,我们在这次实验中将尝试加入用户注册的功能,在客户端为用户提供注册的选项,并将用户注册的账号、密码发送到服务器端,并保存在服务器的 MySQL 数据库中。

知识点

  • MySQL 数据库基础知识
  • 使用 SQL 语句操作数据库
  • C++ 连接数据库并进行操作

数据库一般分为关系型数据库和非关系型数据库,关系型数据库有我们常见的 MySQL、Oracle 等,非关系型数据库有 Redis 等。

特点

关系型数据库是用二维表格模型来组织数据信息的数据库。MySQL 实际上是一个关系数据库管理系统,在这个系统里面可以有多个数据库,每个数据库中可以有多张表,每张表中可以有多列属性和多行数据。

比如我们要添加用户注册功能,就可以创建一个专用的数据库,在数据库中建一张叫 USER 的表,表中包含用户名 NAME 和密码 PASSWORD 两列属性,然后下面的若干行就是具体的数据,如下:

SQL 结构化查询语言

SQL 即结构化查询语言,是一种数据库查询语言,用来管理关系数据库系统,以及对数据进行增删查改等操作。

Linux 下启动 MySQL

我们实验所用的桌面容器已经安装好了 MySQL,因此不需要自己安装。

首先启动 MySQL 服务,如下:

sudo service mysql start

C++连接 MySQL 的环境配置

更新源并安装 dev 组件:

sudo apt update
sudo apt install -y libmysqlclient-dev

安装完毕之后查看 /usr/include/mysql 目录下,是否有 mysql.h 文件,如果有的话就说明安装成功:

ls /usr/include/mysql|grep mysql.h

要求

在之前实验的服务器客户端代码中增添用户注册的功能,在客户端为用户提供注册的选项,并将用户注册的账号、密码发送到服务器端,并保存在服务器的 MySQL 数据库中。

  1. 在服务器本地 Mysql 中创建新数据库 ChatProject,库中有一张表叫 USER,该表中有账号 NAME 和密码 PASSWORD 两项属性。
  2. 为客户端添加注册功能,让用户注册账号密码,客户端将注册信息发送到服务器端,注意:当用户注册时应输入两次密码,如果密码不一致需要重新输入。
  3. 服务器端接收来自客户端的注册信息,将用户的账号、密码写入本地 Mysql 数据库。
  4. 要面向对象编程,进行类封装。

具体实现

首先在 MySQL 控制台创建数据库 ChatProject,如下:

create database ChatProject;

Linux下使用c++实现聊天室相关推荐

  1. 利用多线程实现linux下C语言的聊天室程序:

    转载:http://www.360doc.com/content/16/0421/11/478627_552531090.shtml 利用多线程实现linux下C语言的聊天室程序: 客户端代码: th ...

  2. 基于Linux下的即时通讯聊天室项目(全代码 有注释 可直接运行)

    基于Linux下的即时通讯聊天室项目 一.序言 二.具体功能 三.系统客户要求 四.具体代码 1.服务器代码 2.客户端代码 一.序言 最近在写一个基于Linux下的聊天工具 它适合于局域网内所有人进 ...

  3. Linux下socket多人聊天室

    目录 前言 一.聊天室的实验内容 二.逐个功能的简单分析 三.系统功能模块分解图 1.服务端功能模块图 2.客户端功能模块图 3.守护进程功能模块图 四.功能模块流程图 1.服务端流程图 2.客户端流 ...

  4. Linux C多人网络聊天室

    经过好几天的日夜奋斗,总算把这个聊天室给做出来了,虽然说不上多好,但也是这几天从早到晚劳动的成功,所以就写这篇博文来记录一下啦.别的不敢说,确保能用就是了,完整代码在最后哦~ 当然啦,如果有幸被转发, ...

  5. linux中用gtk编写的聊天室能运行的,CHAT_ROOM

    CHAT_ROOM 所属分类:Linux/Unix编程 开发工具:Unix_Linux 文件大小:191KB 下载次数:7 上传日期:2016-05-25 16:57:56 上 传 者:褚克珍 说明: ...

  6. Linux网络编程之PHP聊天室Workerman-chat

    云服务器上搭建"PHP聊天室框架" 一.简介: 在服务器上搭建PHP聊天室框架{workerman-chat}具体步骤 1. 准备云服务器 购买阿里云服务器 [可选购买其他云服务器 ...

  7. Linux C/TCP多线程网络聊天室

    多线程TCP网络聊天室 我们都知道TCP是一种基于连接的传输协议,使用多线程来实现其实步骤很简单. 首先说明一下项目:服务端开启后,直接运行客户端可以加入聊天室,同时其他客户端可以接收到某某客户端进入 ...

  8. linux命令行聊天,Linux 下使用talk 进行聊天

    Linux中talk命令参数程序用于Internet上两个用户之间进行"交谈":通过键盘输入"说话",通过看终端屏幕"聆听".Linux中t ...

  9. 跨跃平台交流无极限——linux下如何使用即时聊天工具,跨跃平台 交流无极限——Linux下如何使用即时聊天工具(一)...

    I技应 术用与 跨跃平台交流无极限 维普资讯 http://doc.xuehai.net o n I s o f i e n t@ c n i i c o m t . L n x下如何使用即时聊天工具 ...

最新文章

  1. stm32F042 (二) 按键触发中断
  2. C# Socket使用代理 及 Http协议、Socks5协议
  3. 查看.Net Framework版本号
  4. BOOST_VMD_ASSERT_IS_NUMBER宏相关的测试程序
  5. 纪中C组模拟赛总结(2019.9.7)
  6. ruby 怎么抛异常_Ruby中的异常处理
  7. [BZOJ 2594] [Wc2006]水管局长数据加强版 【LCT】
  8. openjudge 二叉树 2756
  9. matlab 计算数据转折点,计算轨迹(路径)中的转折点/枢轴点
  10. 计算机分级无法度量视频,雨林木风win7旗舰版电脑评分时出现无法度量视频播放性能...
  11. Android:adb 详细介绍
  12. 小区物业管理系统设计思想+源码
  13. php网上阅卷源码,翰林金榜网上阅卷
  14. 她每天吃一个煮熟的苹果,从此打开了通往新世界的大门~
  15. 浏览器市场占有率最新分析
  16. 调色盘——将真彩色图像降级为低分辨率图像的八叉树算法
  17. 【编程题训练】统计射击比赛成绩
  18. 【Python网络蜘蛛 · 6】:明文和密文的转换、json模块的使用、jsonpath模块的使用(附源代码)
  19. webservice报错Message part refundRequest was not recognized. (Does it exist in service WSDL?)
  20. Prometheus入门使用(三)

热门文章

  1. CMI的C语言实现方法,C语言——Cmidiv0.6自主和弦模块(测试版)
  2. 两种方法,计算带地形起伏的地表面积
  3. Xcode13在M1 Apple CPU上配置
  4. display:grid 网络布局
  5. 计算机毕业设计JAVA靓车汽车销售网站mybatis+源码+调试部署+系统+数据库+lw
  6. Delphi中ExtractFilePath()及其相近函数用法
  7. 安卓桌面软件_银河麒麟即时通讯工具教程02:安卓应用安装、启动和卸载
  8. 2022年,我们为什么要学习C++?(文末附完整C++后端学习路线)
  9. 魔兽怀旧服转服服务器维护多久,魔兽世界怀旧服转服服务开启 怀旧服转服条件一览...
  10. 禁用 spotlight_如何在Mac,iPhone和iPad上禁用Spotlight的Web搜索