一步一步创建聊天程序1-利用进程和共享内存来创建简易聊天程序
程序员文章站
2022-11-06 23:27:17
最近学习了linux关于进程间通信的相关知识,所以决定借助进程和共享内存,并按照生产者消费者模型来创建一个简易聊天程序。下面简单的说一下程序的思路。 首先是服务端程序,服务端会创建两个进程,进程1负责接收客户端传送过来的消息,并存储起来。进程2负责读取进程1存取的消息。这里使用到了生产者和消费者编程 ......
最近学习了linux关于进程间通信的相关知识,所以决定借助进程和共享内存,并按照生产者消费者模型来创建一个简易聊天程序。下面简单的说一下程序的思路。
首先是服务端程序,服务端会创建两个进程,进程1负责接收客户端传送过来的消息,并存储起来。进程2负责读取进程1存取的消息。这里使用到了生产者和消费者编程模型,并声明了如下的消息存放结构体。
struct message { int target_id; char buf[100]; }; //消息仓库 struct canku { struct message recv_message[10]; int read_pos,write_pos; }messages;
可以看到,我在这里强制消息仓库最多只能存放10条消息(其实只能存放9条,和生产者消费者模型有关)。
生产者和消费者模型:利用read_pos和write_pos来分别标记读取和写入的位置,每次读取和写入后,read_pos或write_pos都会递增1,当read_pos或write_pos到达数组末尾时,就将其重置为0,类似于一个环形链表,可这会出现一个问题:如何判断链表是否为空,或链表是否已经被写满了?
利用read_pos(消息读取的位置)和write_pos(消息写入的位置)来判断消息仓库是否为空和已满。消息仓库在空和满两种状态时,read_pos与write_pos都相等,这样明显不满足我们的要求。所以,空出一个消息位,来利用如下条件判断消息是否空或满。
- 消息为空时
if((messages_ptr->read_pos)%10==messages_ptr->write_pos) { //printf("buff is empty\n"); shmdt(messages_ptr); continue; }
- 消息仓库已满时
if((messages_ptr->write_pos+1)%10==messages_ptr->read_pos) { shmdt(messages_ptr); continue; }
同时,我们还借助于共享内存,将消息仓库映射到共享内存上,这样,进程1和进程2都可以访问消息仓库。
客户端比服务端简单,我创建了一个子进程用来读取服务端转发的其他客户端发来的消息。父进程则用来读取客户输入,将消息发送到服务器。
代码如下:
服务端:
#include <stdio.h> #include <pthread.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <netinet/in.h> #include <errno.h> #define max_listen 10 struct message { int target_id; char buf[100]; }; //消息仓库 struct canku { struct message recv_message[10]; int read_pos,write_pos; }messages; //messages初始化 void init() { messages.read_pos=0; messages.write_pos=0; } //messages销毁 void finish() { messages.read_pos=0; messages.write_pos=0; } int sd; int main() { init(); struct sockaddr_in server_ip,customer_ip; int err; sd=socket(af_inet,sock_stream,0); if(sd==-1) { printf("socket failed\n"); close(sd); return -1; } //server_ip初始化 server_ip.sin_family=af_inet; server_ip.sin_port=htons(5678); server_ip.sin_addr.s_addr=htonl(inaddr_any); memset(server_ip.sin_zero,0,8); err=bind(sd,(struct sockaddr *)(&server_ip),sizeof(struct sockaddr)); if(err==-1) { printf("bind failed\n"); close(sd); return -1; } err=listen(sd,max_listen); if(err==-1) { printf("listen failed\n"); close(sd); return -1; } int length=sizeof(customer_ip); //初始化共享变量,大小等于canku by luke int shmid=shmget(ipc_private,sizeof(struct canku),ipc_creat|0777); if(shmid<0) { printf("shmget failed\n"); return -1;; } struct canku * messages_ptr=&messages; while(1) { int temp_cd=accept(sd,(struct sockaddr *)(&customer_ip),&length); if(temp_cd==-1) { printf("accept failed,ereno: %d\n",temp_cd); close(sd); return -1; } printf("user %d online\n",temp_cd); pid_t pid=fork(); if(pid==0)//子进程 by luke { while(1) { messages_ptr=shmat(shmid,null,0); if((messages_ptr->write_pos+1)%10==messages_ptr->read_pos) { shmdt(messages_ptr); continue; } struct message temp_message; err=recv(temp_cd,&temp_message,sizeof(struct message),0); //err=read(temp_cd,&(recv_message[count-1]),sizeof(struct message)); if(err!=-1) { messages_ptr->recv_message[messages_ptr->write_pos].target_id=temp_message.target_id; strcpy(messages_ptr->recv_message[messages_ptr->write_pos].buf,temp_message.buf); printf("recv: read_pos: %d, write_pos: %d, target_id: %d, buf: %s\n",messages_ptr->read_pos,messages_ptr->write_pos+1,messages_ptr->recv_message[messages_ptr->write_pos].target_id,messages_ptr->recv_message[messages_ptr->write_pos].buf); messages_ptr->write_pos++; if(messages_ptr->write_pos==9) messages_ptr->write_pos=0; } shmdt(messages_ptr); } } //防止主线程被while住,无法接受新的连接请求。 pid_t pid1=fork(); if(pid1==0) { while(1) { messages_ptr=shmat(shmid,null,0); if((messages_ptr->read_pos)%10==messages_ptr->write_pos) { //printf("buff is empty\n"); shmdt(messages_ptr); continue; } //strcpy(messages_ptr->recv_message[messages_ptr->read_pos].buf,"hello"); err=send(messages_ptr->recv_message[messages_ptr->read_pos].target_id,messages_ptr->recv_message[messages_ptr->read_pos].buf,100,0); if(err==-1) { //printf("send failed\n"); } else { printf("send: read_pos: %d, write_pos: %d ,message.target_id: %d, message.buf: %s\n",messages_ptr->read_pos+1,messages_ptr->write_pos,messages_ptr->recv_message[messages_ptr->read_pos].target_id,messages_ptr->recv_message[messages_ptr->read_pos].buf); messages_ptr->read_pos++; if(messages_ptr->read_pos==9) messages_ptr->read_pos=0; } shmdt(messages_ptr); } } } close(sd); shmctl(shmid,ipc_rmid,null); finish(); return 0; }
客户端:
#include <stdio.h> #include <pthread.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> struct message { int target_id; char buf[100]; }; int sd; struct message send_message; void * read_message(void * argv) { while(1) { //读服务器发来的消息 char revbuf[100]; read(sd,revbuf,100); printf("recevice from server: %s",revbuf); } } void * write_message(void * argv) { while(1) { printf("input message: \n"); memset(send_message.buf,0,128); send_message.target_id=-1; scanf("%d %s",&send_message.target_id,send_message.buf); write(sd,&send_message,sizeof(send_message)); sleep(3); } } int main() { struct sockaddr_in server_ip,customer_ip; int err; sd=socket(af_inet,sock_stream,0); if(sd==-1) { printf("socket failed\n"); close(sd); return -1; } //server_ip初始化 server_ip.sin_family=af_inet; server_ip.sin_port=htons(5678); server_ip.sin_addr.s_addr=htonl(inaddr_any); //err=inet_aton("115.157.201.179",&server_ip.sin_addr.s_addr); memset(server_ip.sin_zero,0,8); err=connect(sd,(struct sockaddr *)(&server_ip),sizeof(server_ip)); if(err==-1) { printf("connect failed\n"); close(sd); return -1; } pid_t pid=fork(); if(pid==0) { while(1) { //读服务器发来的消息 //printf("read message: \n"); char revbuf[100]; recv(sd,revbuf,100,0); //read(sd,revbuf,100); printf("recevice from server: %s\n",revbuf); } } while(1) { printf("input message: \n"); memset(send_message.buf,0,128); send_message.target_id=-1; scanf("%d %s",&send_message.target_id,send_message.buf); if(send_message.target_id!=-1&&(strcmp(send_message.buf,"")!=0)) { err=send(sd,&send_message,sizeof(send_message),0); if(err==-1) { printf("send failed\n"); } //write(sd,&send_message,sizeof(send_message)); send_message.target_id=-1; memset(send_message.buf,0,sizeof(send_message.buf)); } sleep(3); } close(sd); return 0; }