技术要点:
1. 使用命名管道,实现进程间管道的访问
2. 管道操作使用长连接
3. 使用信号量共享key实现进程锁
Linux管道是单向的,要实现进程间双向通讯,需要使用两个管道:一个负责Client发送数据到server,另一个负责server返回数据到Client;对于一个client访问server的情况(相当于一个进程访问server),Client发送与接收数据不会有问题;对于多个Clinet访问server的情况(相当于多个进程访问server),此时,各个client发送数据没有问题,接收数据错乱,原因为没有保证每个进程发送与接收的同步,解决方法,使用进程锁
见代码
//server
#include <QCoreApplication>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
using namespace std;
volatile int sem_id;
struct sembuf sem_op;
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
void sem_lock(int sem_id, struct sembuf *sem_op)
{
// P operate
sem_op->sem_num = 0;
sem_op->sem_op = -1;
sem_op->sem_flg = 0;
semop(sem_id,sem_op,1); //P operate add - 1 per time
}
void sem_unlock(int sem_id, struct sembuf *sem_op)
{
// V operate
sem_op->sem_num = 0;
sem_op->sem_op = 1;
sem_op->sem_flg = 0;
semop(sem_id,sem_op,1); //V operate add + 1 per time
}
#define PIPE_NAME_ONE "/tmp/snbc1_fifo"
#define PIPE_NAME_TWO "/tmp/snbc2_fifo"
pthread_mutex_t g_mutex;
pthread_mutex_t g_threadMutex;
volatile int nRetPipe1 = 0;
volatile int nRetPipe2 = 0;
void *ReadAndWriteTread(void* arg);
void *WritePipe(void* data);
void *ConnectThread(void* arg)
{
cout << "[%s]线程开始启动"<< __FUNCTION__ << endl;
if(-1 == access(PIPE_NAME_ONE,F_OK) )
{
remove(PIPE_NAME_ONE);
nRetPipe1= mkfifo(PIPE_NAME_ONE,O_CREAT|O_RDWR|0666);
if(0 > nRetPipe1)
{
cout << "线程创建PIPE1 fail" << endl;
}
}
if(-1 == access(PIPE_NAME_TWO,F_OK) )
{
remove(PIPE_NAME_TWO);
int nPipe2= mkfifo(PIPE_NAME_TWO,O_CREAT|O_RDWR|0666);
if(0 > nPipe2)
{
cout << "线程创建PIPE fail" << endl;
}
}
cout << "线程开始打开PIPE1" << endl;
nRetPipe1 = open(PIPE_NAME_ONE,O_RDONLY);
nRetPipe2 = open(PIPE_NAME_TWO,O_WRONLY | O_TRUNC);
if(nRetPipe1 > 0 && nRetPipe2 > 0)
{
cout << "线程开始打开PIPE1成功" << endl;
pthread_t pThread = 0;
void *res;
int nLRet = 0;
nLRet = pthread_create(&pThread, NULL,ReadAndWriteTread, (void*)&nRetPipe1);
if(0 != nLRet)
{
cout << "创建线程InstanceThread失败!" << endl;
}
nLRet = pthread_join(pThread,&res);
if(0 != nLRet)
{
cout << "创建线程InstanceThread失败!" << endl;
}
cout << "线程执行完成退出 " << endl;
}
else
{
cout << "打开PIPE1失败!" <<endl;
close(nRetPipe1);
close(nRetPipe2);
}
cout << "线程正常退出" << endl;
return 0;
}
void *ReadAndWriteTread(void* arg)
{
int hTmpPipe = *((int*) arg);
while(1)
{
unsigned char ucRequest[1024] = {0};
unsigned char ucReply[1024] = {0};
long dwRequestLen = 0;
long dwReplyLen = 0;
if(hTmpPipe < 0)
{
hTmpPipe = open(PIPE_NAME_ONE,O_RDONLY);
}
if(hTmpPipe > 0)
{
dwRequestLen = read(hTmpPipe, ucRequest, sizeof(ucReply));
if(-1 == dwRequestLen || 0 == dwRequestLen )
{
cout << "读取数据失败或数据为空 "<<endl;
}
else
{
cout << "读取管道一的数据dwRequestLen = %d"<< dwRequestLen<< endl;
cout << "读取管道一的数据ucRequest =%s" << ucRequest <<endl;
pthread_mutex_lock(&g_mutex);
usleep(100);
pthread_mutex_unlock(&g_mutex);
WritePipe((void*)ucRequest);
}
}
else
{
close(hTmpPipe);
}
}
}
void *WritePipe(void* data)
{
cout <<"WritePipeThread 线程开始执行" <<endl;
cout << data <<endl;
if(nRetPipe2 < 0)
{
nRetPipe2 = open(PIPE_NAME_TWO,O_WRONLY | O_TRUNC);
}
if(nRetPipe2 > 0)
{
cout << "打开PIPE2成功" <<endl;
long dwRequestLen = 1024;
long dwWritten = 0;
dwWritten = write(nRetPipe2, data, dwRequestLen );
if(-1 == dwWritten || dwRequestLen != dwWritten)
{
cout << " 写入数据出错或无数据写入!!!!!" << endl;
}
else
{
cout << "写入PIPE2成功" << endl;
}
}
else
{
cout << "打开PIPE2失败" << endl;
close(nRetPipe2);
}
cout << "执行完成"<<endl;
return 0;
}
bool initSemIDAndSemVal(union semun sem_val)
{
int rc = 0;
key_t sem_key;
sem_key = ftok("/dev/null",1);
if(-1 == sem_key)
{
cout << "Create sem_key fail" <<endl;
return false;
}
sem_id = semget(sem_key,1,IPC_CREAT | 0600);
if(-1 == sem_id)
{
cout << "Create sem_id fail" <<endl;
return false;
}
sem_val.val = 1;
rc = semctl(sem_id,0,SETVAL,sem_val);
if(-1 == rc)
{
cout << "Create semctl fail" <<endl;
return false;
}
return true;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
pthread_mutex_init(&g_mutex,0);
pthread_mutex_init(&g_threadMutex,0);
struct sembuf sem_op;
union semun sem_val;
if(initSemIDAndSemVal(sem_val))
{
cout << "initSemIDAndSemVal success" <<endl;
}
else
{
cout << "initSemIDAndSemVal fail" <<endl;
}
pthread_t hConnectThread = NULL;
int nRet = 0;
nRet = pthread_create(&hConnectThread,NULL,ConnectThread,NULL);
if(0 != nRet)
{
cout << "Create ConnectThread fail" << endl;
}
pthread_mutex_destroy(&g_mutex);
pthread_mutex_destroy(&g_threadMutex);
return a.exec();
}
//Client1
#include <QCoreApplication>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
using namespace std;
#define PIPE_NAME_ONE "/tmp/snbc1_fifo"
#define PIPE_NAME_TWO "/tmp/snbc2_fifo"
#define TestCount 10000
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
volatile int sem_id;
int get_sem_val(int sid, int semnum)
{
return (semctl(sid,semnum,GETVAL,0));
}
void sem_lock(int sem_id, struct sembuf *sem_op)
{
// P operate
sem_op->sem_num = 0;
sem_op->sem_op = -1;
sem_op->sem_flg = 0;
semop(sem_id,sem_op,1); //V operate add + 1 per time
}
void sem_unlock(int sem_id, struct sembuf *sem_op)
{
// V operate
sem_op->sem_num = 0;
sem_op->sem_op = 1;
sem_op->sem_flg = 0;
semop(sem_id,sem_op,1); //V operate add + 1 per time
}
bool initSemID()
{
int rc = 0;
key_t sem_key;
sem_key = ftok("/dev/null",1);
if(-1 == sem_key)
{
cout << "Create sem_key fail" <<endl;\
return false;
}
sem_id = semget(sem_key,1,IPC_CREAT | 0600);
if(-1 == sem_id)
{
cout << "Create sem_id fail" <<endl;
return false;
}
return true;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
struct sembuf sem_op;
union semun sem_val;
if(initSemID())
{
cout << "initSendID success" << endl;
}
else
{
cout << "initSendID fail" <<endl;
}
int fd1=-1, fd2 = -1, fd3= -1 ;
char buf[1024] = "";
char bufSmall[20]="";
char bufWrite[1024] = "";
cout << "Clent Open Start"<< endl;
char tempPipeName2[64] ={0};
if((fd1 = open(PIPE_NAME_ONE,O_WRONLY | O_TRUNC) )< 0)
{
cout << "Client open PIPE_NAME_ONE failed " << endl;
return -1;
}
if((fd2 = open(PIPE_NAME_TWO,O_RDONLY)) < 0)
{
cout << "Open PIPE_NAME_TWO failed" << endl;
}
for(int i = 0; i < TestCount; i++)
{
cout << sem_id <<endl;
sem_lock(sem_id, &sem_op);
cout << "Client1 start getLock" <<endl;
cout << "线程创建PIPE2完成" << endl;
if(fd1 < 0)
{
fd1 = open(PIPE_NAME_ONE,O_WRONLY | O_TRUNC) ;
}
sprintf(bufWrite,"Hello Server, I am client1 Data is %d", i);
cout <<"Send: "<<bufWrite << endl;
if((write(fd1,bufWrite ,strlen(bufWrite))) < 0)
{
cout << "write buf failed" <<endl;
return -1;
}
if(fd2 < 0)
{
fd2 = open(PIPE_NAME_TWO,O_RDONLY);
}
int nRet = 0;
nRet = read(fd2,buf,1024);
cout << "Receive: "<<buf <<endl;
sem_unlock(sem_id,&sem_op);
cout << "Client1 unLock" <<endl;
}
close(fd1);
close(fd2);
return a.exec();
}
//Client2
#include <QCoreApplication>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <fcntl.h>
#include <sys/stat.h>
using namespace std;
#define PIPE_NAME_ONE "/tmp/snbc1_fifo"
#define PIPE_NAME_TWO "/tmp/snbc2_fifo"
#define TestCount 10000
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
volatile int sem_id;
void sem_lock(int sem_id, struct sembuf *sem_op)
{
// P operate
sem_op->sem_num = 0;
sem_op->sem_op = -1;
sem_op->sem_flg = 0;
semop(sem_id,sem_op,1); //P operate add - 1 per time
}
void sem_unlock(int sem_id, struct sembuf *sem_op)
{
// V operate
sem_op->sem_num = 0;
sem_op->sem_op = 1;
sem_op->sem_flg = 0;
semop(sem_id,sem_op,1); //V operate add + 1 per time
}
bool initSemID()
{
cout << "read data" << endl;
int rc = 0;
key_t sem_key;
sem_key = ftok("/dev/null",1);
if(-1 == sem_key)
{
cout << "Create sem_key fail" <<endl;
return false;
}
sem_id = semget(sem_key,1,IPC_CREAT | 0600);
if(-1 == sem_id)
{
cout << "Create sem_id fail" <<endl;
return false;
}
return true;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
struct sembuf sem_op;
union semun sem_val;
if(initSemID())
{
cout << "initSendID success" << endl;
}
else
{
cout << "initSendID fail" <<endl;
}
int fd1 = -1, fd2 = -1;
char buf[1024] = "";
char bufSmall[20]="";
char bufWrite[1024] = "";
if((fd1 = open(PIPE_NAME_ONE,O_WRONLY | O_TRUNC) )< 0)
{
cout << "Client open PIPE_NAME_ONE failed " << endl;
return -1;
}
if((fd2 = open(PIPE_NAME_TWO,O_RDONLY)) < 0)
{
cout << "Open PIPE_NAME_TWO failed" << endl;
return -1;
}
for(int i = 0; i < TestCount; i++)
{
cout << sem_id <<endl;
sem_lock(sem_id,&sem_op);
cout << "Client2 start Lock" <<endl;
sprintf(bufWrite,"Hello Server, I am client2 Data is %d",i);
if(fd1 < 0)
{
fd1 = open(PIPE_NAME_ONE,O_WRONLY | O_TRUNC) ;
}
cout <<"Send: "<< bufWrite <<endl;
if((write(fd1, bufWrite,strlen(bufWrite))) < 0)
{
cout << "write buf failed" <<endl;
return -1;
}
if(fd2 < 0)
{
fd2 = open(PIPE_NAME_TWO,O_RDONLY);
}
int nRet = 0;
nRet = read(fd2,buf,1024);
cout <<"Receive: "<<buf <<endl;
sem_unlock(sem_id,&sem_op);
cout << "Client2 unLock" <<endl;
}
close(fd1);
close(fd2);
return a.exec();
}