简单逻辑
客户端发送数据,服务器接收数据,可以通过ctrl+c或客户端输入quit进行停止通信
注意点
1.有名管道的实现
2.strlen(),sizeof()对字符串的处理
3.缓存溢出
代码及实现
makefile
.PHONY : all
all : server client
server : qq_server.cpp
g++ -std=c++11 qq_server.cpp -o server
client : qq_client.cpp
g++ -std=c++11 qq_client.cpp -o client
.PHONY:clean
clean:
rm -f server client
头文件comm.hpp
#pragma once
#include <stdint.h>
#include <iostream>
#include <string>
const std::string fifoname="./fifo";
//所有用户有读写权限
uint32_t mode =0666;
#define NUM 1024
客户端qq_client.cpp
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include "comm.hpp"
int main(){
int wfd=open(fifoname.c_str(),O_WRONLY);
if(wfd<0){
std::cout<<"have error :"<<strerror(errno)<<std::endl;
return 1;
}
char buffer[NUM];
while(true){
std::cout<<"请输入消息: ";
//fgets结尾会自动加入\0
char* msg = fgets(buffer,sizeof(buffer),stdin);
//fgets接收数据后结尾为\n\0
//断言空指针
assert(msg);
(void)msg;
//防止回车被读入缓冲区
//将回车位置替换为0及结尾为\0
buffer[strlen(buffer)-1]=0;
//当客户端输入quit时结束循环
if(strcasecmp(buffer,"quit")==0){
break;
}
ssize_t n=write (wfd,buffer,strlen(buffer));
assert(n>=0);
(void)n;
}
close(wfd);
return 0;
}
服务器端qq_server.cpp
#include <unistd.h>
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include "comm.hpp"
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <typeinfo>
volatile sig_atomic_t flag =0;
void funsig(int signum){
flag= 1;
}
int main(){
umask(0);
//int mkfifo(const char *pathname, mode_t mode);
//c_str()将stirng->char
int n=mkfifo(fifoname.c_str(),mode);
if(n!=0){
std::cout<<"have error :"<< strerror(errno)<<std::endl;
return 1;
}
//std::cout<<"create fifo success"<<std::endl;
int rfd=open(fifoname.c_str(),O_RDONLY);
if(rfd<=0){
std::cout<<"have error :"<<strerror(errno)<<std::endl;
return 2;
}
//std::cout<<"open fifo success"<<std::endl;
signal(SIGINT,funsig);
char buffer[NUM];
while(true){
//初始化
buffer[0]=0;
if(1==flag){
break;
}
//不读入\0
ssize_t n=read(rfd,buffer,sizeof(buffer)-1);
if(n>0){
//在数据结尾放\0,按c语言标准打印,c语言字符串必须以\0结尾
buffer[n]=0;
std::cout<<buffer<<std::endl;
}
else if(n==0){
std::cout<<"client quit"<<std::endl;
break;
}
else{
std::cout<<"have error :"<<strerror(errno)<<std::endl;
break;
}
}
close(rfd);
unlink(fifoname.c_str());
return 0;
}
总结
- strlen、sizeof区别
- strlen 遇到 \0 停止读取,取值不计算 \0
- sizeof 取值将 \0 计算在内
int main() {
char val[] = "asd\0";
char val2[] = { 1,2,3,NULL,4,5,0 };
cout << sizeof(val) << endl;//5
cout << strlen(val) << endl;//3
cout << sizeof(val2) << endl;//7
cout << strlen(val2) << endl;//3
return 0;
}
- sizeof 内存中占用的字节数
- strlen 实际长度
char str[20]=”0123456789”;
int a=strlen(str); // a=10
int b=sizeof(str) // b=20
- volatie 和 sig_atomic_t
- volatie
告诉编辑器数据随时会发生变化,使用前需要从地址中取值 - sig_atomic_t
确保在信号处理过程中可以原子地访问变量。
原子访问优点:
信号处理器可能在程序的任何时刻中断正常的执行流程。使用sig_atomic_t类型声明的变量可以确保在信号处理器中对其进行读取和修改时,这些操作是原子的。原子操作意味着在执行期间不会被其他信号中断,从而避免了数据竞争和不确定的行为。