Linux下使用c++实现聊天室 您所在的位置:网站首页 ubuntu系统聊天软件 Linux下使用c++实现聊天室

Linux下使用c++实现聊天室

2024-06-03 00:00| 来源: 网络整理| 查看: 265

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.gz tar -zxvf gdb-8.0.1.tar.gz cd gdb-8.0.1/ ./configure make sudo make install gdb -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,依赖文件3 g++ 编译命令 目标名2:依赖文件4,依赖文件5 g++ 编译命令

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

target:a.cpp b.o g++ -o test a.cpp b.o b.o:b.cpp g++ -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.o g++ -o test a.cpp b.o b.o:b.cpp g++ -c b.cpp .PHONY:clean clean: rm *.o rm 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 #include #include #include #include #include #include #include #include #include #include using namespace std; int main() { //定义sockfd int server_sockfd = socket(AF_INET,SOCK_STREAM, 0); //定义sockaddr_in struct 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,出错返回-1 if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1) { perror("bind");//输出错误原因 exit(1);//结束程序 } //listen,成功返回0,出错返回-1 if(listen(server_sockfd,20) == -1) { perror("listen");//输出错误原因 exit(1);//结束程序 } //客户端套接字 struct sockaddr_in client_addr; socklen_t length = sizeof(client_addr); //成功返回非负描述字,出错返回-1 int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length); if(conn


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有