使用多进程实现多任务
程序移植时需要注意ziji电脑的IP地址与端口,
案例:
客户端:
1. 建立socket
2. 连接到服务器
3. 创建curses界面
4. 创建子进程
5. 在父进程中,输入,发送聊天信息
6. 在子进程中,接收服务器传递的信息(服务器把接收的信息进行广播)。
// chatCient.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <curses.h>
#include <unistd.h>
#include <signal.h>
/*
一般写curses 的程序,就不要用printf来进行输出了
*/
WINDOW *winfo,*wmsg;
int fd;
int r;
struct sockaddr_in dr;
int initSocket(); // 创建文件描述符,绑定IP地址
void initUI(); // 初始化字符界面
void destroy();
void deal(int s)
{
int status;
wait(&status);
destroy();
exit(-1);
}
main()
{
initUI();
r = initSocket();
if(r==-1) exit(-1);
signal(SIGCHLD,deal);
if(fork()) // 父进程
{
// 输入,发送聊天信息
char buf[256];
while(1)
{
mvwgetstr(wmsg,1,1,buf);
wmove(wmsg,1,1);
clrtoeol();
//wclrtobot(wmsg);
wclrtoeol(wmsg);
send(fd,buf,strlen(buf),0);
refresh();
wrefresh(wmsg);
wrefresh(winfo);
}
}
else /// 子进程,接收来自服务器的数据,并显示
{
char buf[256];
int line = 1; // 行数初始化显示在第一行
while(1)
{
r = recv(fd,buf,255,0);
if(r == -1) break;
if(r == 0) break;
buf[r] = 0;
mvwaddstr(winfo,line,1,buf);
line++;
if(line >= (LINES-3))
{
wclear(winfo);
line = 1;
box(winfo,0,0);
}
//wclear(wmsg);
wmove(wmsg,1,1);
//clrtoeol();
touchwin(wmsg);
refresh();
wrefresh(winfo);
wrefresh(wmsg);
}
exit(-1);
}
destroy();
}
// 函数调用成功就返回0,调用失败返回-1
int initSocket()
{
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd == -1) return -1;
dr.sin_family = AF_INET;
dr.sin_port = htons(9999);
dr.sin_addr.s_addr = inet_addr("120.6.20.247");
r = connect(fd,(struct sockaddr*)&dr,sizeof(dr));
if(r==-1)
{
close(fd);
return -1;
}
return 0;
}
void initUI()
{
initscr();
winfo = derwin(stdscr,(LINES-3),COLS,0,0);
wmsg = derwin(stdscr,3,COLS,LINES-3,0);
box(winfo,0,0);
box(wmsg,0,0);
refresh();
wrefresh(winfo);
wrefresh(wmsg);
}
void destroy()
{
close(fd);
endwin();
}
服务器程序:
建立socket
绑定地址
监听
循环接收客户连接
为客户创建子进程
在子进程接收该客户的数据,并进行广播。
/// chatserver.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/mman.h>
int sfd;
int *fds; // 存放存放客户代理描述符,使用共享内存
int idx =0; // 客户在数组中的下标
struct sockaddr_in dr; // 客户ID
int r;
main()
{
// 1. 建立服务器socket
fds = mmap(0,4*100,PROT_READ | PROT_WRITE,MAP_ANONYMOUS | MAP_SHARED,0,0);
bzero(fds,sizeof(fds));
sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd == -1) printf("socket err:%m\n"),close(sfd),exit(-1);
printf("socket OK!\n");
// 2. 绑定地址
dr.sin_family = AF_INET;
dr.sin_port = htons(9999);
dr.sin_addr.s_addr = inet_addr("120.6.20.247");
r = bind(sfd,(struct sockaddr*)&dr,sizeof(dr));
if(r==-1) printf("bind err:%m\n"),exit(-1);
printf("bind OK!\n");
// 3. 监听
r = listen(sfd,5); //队列中可以容纳的未处理连接的最大数目做出限制。常用为5
if(r == -1) printf("listen err:%m\n"),exit(-1);
printf("listen OK!\n");
// 4. 循环接收客户连接
while(1)
{
fds[idx] = accept(sfd,0,0);
if(fds[idx] == -1) break;
printf("有客户连接:%d\n",fds[idx]);
// 5. 建立一个子进程
if(fork())
{
idx++;
continue;
}
else
{
// 6. 子进程任务: 接收客户信息并且广播
char buf[256];
int i;
printf("开始接收客户%d数据\n",fds[idx]);
while(1)
{
// 接收客户数据
r = recv(fds[idx],buf,255,0);
//printf("%d\n",r);
if(r == 0)
{
printf("客户%d退出!\n",fds[idx]);
close(fds[idx]);
fds[idx] = 0;
break;
}
if( r== -1)
{
printf("网络故障!\n");
close(fds[idx]);
fds[idx] = 0;
break;
}
buf[r] = 0;
printf("来自客户%d的数据:%s\n",fds[idx],buf);
// 广播
for(i=0; i<100;++i)
{
if(fds[i] > 0)
{
send(fds[i],buf,r,0);
}
}
}
exit(0);
}
}
close(sfd);
}
总结:
多个用户连接并不能全部广播到每个用户,多进程由于进程的资源结构独立,
新进程的文件描述符号的环境在老进程中无法访问。