23,217
社区成员




#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<unistd.h>
#define MYPORT 3490 //侦听端口号
#define BACKLOG 100 //侦听队列长度
#define MAXDATASIZE 1024 //一次可以读的最大的字节数
int main(int argc, char* argv[])
{
int serverfd; //服务器socket套接字描述符
int communicationfd = 0; //双方通信描述符
int link = 0; //连接状态,根据accept函数返回值改变,初始未连接时为0
struct sockaddr_in serveraddr; //服务器地址信息
struct sockaddr_in clientaddr; //客户端地址信息
int clientsinsize; //客户端地址信息结构体大小
//1获得服务器套接字描述符
if((serverfd=socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Server Socket Failed!");
exit(1);
}
//2构造服务器的地址信息sockaddr_in
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(MYPORT); //使用网络字节序端口号
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); //网络字节序IP
bzero(&(serveraddr.sin_zero), 8);
//3绑定侦听端口
if(bind(serverfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr)) == -1)
{
perror("Server Bind Failed!");
exit(1);
}
//4监听端口
if(listen(serverfd, BACKLOG) == -1)
{
perror("Server Listen Failed!");
exit(1);
}
while(link == 0)
{
//5接受客户端的连接请求
printf("等待连接中......\n");
clientsinsize = sizeof(struct sockaddr_in);
communicationfd = accept(serverfd, (struct sockaddr*)&clientaddr, &clientsinsize);
if(communicationfd == -1)
{
perror("Server Accept faild");
exit(1);
}
if(communicationfd > 0) //accept函数返回值大于0时表示接收了连接请求,返回的即是通信所需要的文件描述符
link = 1;
}
int clientip = clientaddr.sin_addr.s_addr;
printf("Got Connection From %d.%d.%d.%d\n", clientip&255,(clientip>>8)&255,(clientip>>16)&255,(clientip>>24)&255);
printf("现在可以开始通信了!\n");
//主循环,接受连接请求后link值变为1
pid_t fpid; //创建子进程,让它处理和父进程不同的工作
fpid = fork();
while(link==1)
{
if(fpid > 0)
{
//6父进程读取消息
char buf[MAXDATASIZE];
int numbytes = recv(communicationfd, buf, MAXDATASIZE, 0);
if(numbytes == -1)
{
perror("Receive Failed!");
exit(1);
}
else if(numbytes > 0)
{
buf[numbytes] = '\0';
printf("对方(Client)发来的消息: \n");
printf("*****");
printf("%s\n", buf);
}
else
{
printf("对方已经关闭连接!\n");
link = 0;
}
}
else
{
//7子进程发送消息
char msg[1024];
char judge[1024] = "quit";
scanf("%s", msg);
if(strcmp(msg, judge)==0)
{
link = 0;
close(serverfd);
close(communicationfd);
printf("连接已经被关闭!\n");
return 0;
}
else
{
printf("我(Server)发送的消息:\n");
printf("*****");
printf("%s\n", msg);
if(send(communicationfd, msg, 1024, 0) == -1)
{
perror("Send Failed");
continue;
}
}
}
}
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<arpa/inet.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<fcntl.h>
#include<unistd.h>
#define PORT 3490
#define MAXDATASIZE 1024 //一次可以读的最大的字节数
int main(int argc, char* argv[])
{
int clientfd, numbytes; //客户端socket描述符和接收到数据大小
int link = 1 ; //表示连接状态,由connect函数赋值,连接时为0,未连接时为1。
char buf[MAXDATASIZE]; //读取的缓冲区
struct sockaddr_in serveraddr; //服务器地址信息结构体
//1创建客户端socket,并获取客户端socket描述符
if((clientfd=socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Client Socket Failed!");
exit(1);
}
//2构造服务器的地址信息sockaddr_in结构
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(PORT); //使用网络字节序端口号
serveraddr.sin_addr.s_addr = inet_addr("192.168.1.101"); //将点分十进制IP地址转化为网络字节序IP
bzero(&(serveraddr.sin_zero), 8);
//3向服务器发起连接
printf("等待服务器响应连接请求......\n");
link = connect(clientfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr));
if(link == -1)
{
perror("Connect Failed!");
exit(1);
}
if(link==0)
printf("已经成功连接至服务器!\n");
else
printf("与服务器连接失败!\n");
//将connect函数返回值赋给link,为0则表示连接成功,开始读取缓冲区内容
pid_t fpid; //创建子进程,让它处理和父进程不同的工作
fpid = fork();
while(link==0)
{
if(fpid > 0)
{
//4父进程读取消息
numbytes = recv(clientfd, buf, MAXDATASIZE, 0);
if(numbytes == -1)
{
perror("Receive Failed!");
exit(1);
}
else if(numbytes > 0)
{
buf[numbytes] = '\0';
printf("对方(Server)发来的消息: \n");
printf("*****");
printf("%s\n", buf);
}
else
{
printf("对方已经关闭连接!\n");
link = 1;
}
}
else
{
//5子进程用来发送消息
char msg[1024];
char judge[1024] = "quit";
scanf("%s", msg);
if(strcmp(msg, judge)==0)
{
close(clientfd);
printf("连接已经被关闭!\n");
link = 1;
}
else
{
printf("我(Client)发送的消息:\n");
printf("*****");
printf("%s\n", msg);
if(send(clientfd, msg, 1024, 0) == -1)
{
perror("Send Failed");
continue;
}
}
}
}
return 0;
}
client.c
//5子进程用来发送消息
char msg[1024];
char judge[1024] = "quit";
scanf("%s", msg);
//if(strcmp(msg, judge)==0)
if(strcmp(msg, judge)==0 || getppid() == 1) //改为这样
{
close(clientfd);
printf("连接已经被关闭!\n");
exit(0);
}
else
{
printf("我(Client)发送的消息:\n");
printf("*****");
printf("%s\n", msg);
if(send(clientfd, msg, 1024, 0) == -1)
{
perror("Send Failed");
continue;
}
}
解决办法:
你两边代码都这样改试试,因为我没调试过;
原理是 父进程退出后,子进程的父进程id会被设置为1, 子进程通过这个来判断父进程是否退出。
延伸思考:
另外,我查了下资料才清楚,我之前觉得你的父进程退出,子进程不应该受影响,应该一直在scanf那里等待数据输入;
翻阅了 <<UNIX环境高级编程>> 第三版 孤儿进程组那一节 后才似乎清楚:
书上的原话是:
“父进程终止后,进程组 ( 我的注释:'进程有一个组id,为父进程的id,虽然子进程的父进程id被设置为1,但是进程组id并没有改变' ) 包含一个停止的进程,进程组成为孤儿进程组,POSIX.1 要求向新孤儿进程组中处于停止状态的没一个进程发送挂断信号(SIGHUP),接着又向其发送继续信号(SIGCONT)
在处理了挂断信号后,子进程继续......”
再结合 http://www.cocoachina.com/articles/84766 这个博客的测试结果来分析;
先是SIGCONT信号唤醒了卡在 scanf 的子进程,然后子进程向下执行的过程中,执行了你的三句printf,之后被SIGHUP信号终止进程。
这里又有个问题了,SIGHUP 为什么没有在执行 printf 之前就终止进程呢,
我猜测是
内核响应SIGCONT信号后,得到了继续运行的命令,然后就继续执行,然后内核处理第二个信号 SIGHUP ,就是说两个信号处理之间是有间隔的,这间隔 和 SIGCONT的默认处理机制【继续运行】的合作 就让你的三句printf 执行了。
你这个小问题让我翻了好几页书,不错不错。
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<unistd.h>
#include<signal.h>
#define MYPORT 3490 //侦听端口号
#define BACKLOG 100 //侦听队列长度
#define MAXDATASIZE 1024 //一次可以读的最大的字节数
void dealsig(int sig)
{
exit(0);
}
int main(int argc, char* argv[])
{
int serverfd; //服务器socket套接字描述符
int communicationfd = 0; //双方通信描述符
int link = 0; //连接状态,根据accept函数返回值改变,初始未连接时为0
struct sockaddr_in serveraddr; //服务器地址信息
struct sockaddr_in clientaddr; //客户端地址信息
int clientsinsize; //客户端地址信息结构体大小
//1获得服务器套接字描述符
if((serverfd=socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Server Socket Failed!");
exit(1);
}
//2构造服务器的地址信息sockaddr_in
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(MYPORT); //使用网络字节序端口号
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); //网络字节序IP
bzero(&(serveraddr.sin_zero), 8);
//3绑定侦听端口
if(bind(serverfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr)) == -1)
{
perror("Server Bind Failed!");
exit(1);
}
//4监听端口
if(listen(serverfd, BACKLOG) == -1)
{
perror("Server Listen Failed!");
exit(1);
}
while(link == 0)
{
//5接受客户端的连接请求
printf("等待连接中......\n");
clientsinsize = sizeof(struct sockaddr_in);
communicationfd = accept(serverfd, (struct sockaddr*)&clientaddr, &clientsinsize);
if(communicationfd == -1)
{
perror("Server Accept faild");
exit(1);
}
if(communicationfd > 0) //accept函数返回值大于0时表示接收了连接请求,返回的即是通信所需要的文件描述符
link = 1;
}
int clientip = clientaddr.sin_addr.s_addr;
printf("Got Connection From %d.%d.%d.%d\n", clientip&255,(clientip>>8)&255,(clientip>>16)&255,(clientip>>24)&255);
printf("现在可以开始通信了!\n");
//主循环,接受连接请求后link值变为1
pid_t fpid; //创建子进程,让它处理和父进程不同的工作
fpid = fork();
pid_t waitchildpid; //用来检测子进程是否结束
if(fpid > 0)
{
while(1)
{
signal(SIGCHLD, dealsig);
waitchildpid=waitpid(-1, 0, WNOHANG);
printf("PID: %d\n",waitchildpid);
if(waitchildpid == 0)
{
//6父进程读取消息
char buf[MAXDATASIZE];
int numbytes = recv(communicationfd, buf, MAXDATASIZE, 0);
if(numbytes == -1)
{
perror("Receive Failed!");
exit(1);
}
else if(numbytes > 0)
{
buf[numbytes] = '\0';
printf("对方(Client)发来的消息: \n");
printf("*****");
printf("%s\n", buf);
}
else
{
printf("对方已经关闭连接!\n");
return 0;
}
}
else
{
return 0;
}
}
}
else
{
while(1)
{
//7子进程发送消息
char msg[1024];
char judge[1024] = "quit";
scanf("%s", msg);
if(strcmp(msg, judge)==0)
{
close(serverfd);
close(communicationfd);
printf("连接已经被关闭!\n");
exit(0);
}
else
{
printf("我(Server)发送的消息:\n");
printf("*****");
printf("%s\n", msg);
if(send(communicationfd, msg, 1024, 0) == -1)
{
perror("Send Failed");
continue;
}
}
}
}
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<arpa/inet.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<fcntl.h>
#include<unistd.h>
#include<signal.h>
#define PORT 3490
#define MAXDATASIZE 1024 //一次可以读的最大的字节数
void dealsig(int sig)
{
exit(0);
}
int main(int argc, char* argv[])
{
int clientfd, numbytes; //客户端socket描述符和接收到数据大小
int link = 1 ; //表示连接状态,由connect函数赋值,连接时为0,未连接时为1。
char buf[MAXDATASIZE]; //读取的缓冲区
struct sockaddr_in serveraddr; //服务器地址信息结构体
//1创建客户端socket,并获取客户端socket描述符
if((clientfd=socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Client Socket Failed!");
exit(1);
}
//2构造服务器的地址信息sockaddr_in结构
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(PORT); //使用网络字节序端口号
serveraddr.sin_addr.s_addr = inet_addr("192.168.1.104"); //将点分十进制IP地址转化为网络字节序IP
bzero(&(serveraddr.sin_zero), 8);
//3向服务器发起连接
printf("等待服务器响应连接请求......\n");
link = connect(clientfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr));
if(link == -1)
{
perror("Connect Failed!");
exit(1);
}
if(link == 0)
printf("已经成功连接至服务器!\n");
else
printf("与服务器连接失败!\n");
//将connect函数返回值赋给link,为0则表示连接成功,开始读取缓冲区内容
pid_t fpid; //创建子进程,让它处理和父进程不同的工作
fpid = fork();
pid_t waitchildpid; //用来检测子进程是否结束
if(fpid > 0)
{
while(1)
{
signal(SIGCHLD, dealsig);
waitchildpid=waitpid(-1, 0, WNOHANG);
printf("PID: %d\n",waitchildpid);
if(waitchildpid == 0)
{
//4父进程读取消息
numbytes = recv(clientfd, buf, MAXDATASIZE, 0);
if(numbytes == -1)
{
perror("Receive Failed!");
exit(1);
}
else if(numbytes > 0)
{
buf[numbytes] = '\0';
printf("对方(Server)发来的消息: \n");
printf("*****");
printf("%s\n", buf);
}
else
{
printf("对方已经关闭连接!\n");
return 0;
}
}
else
{
return 0;
}
}
}
else
{
while(1)
{
//5子进程用来发送消息
char msg[1024];
char judge[1024] = "quit";
scanf("%s", msg);
if(strcmp(msg, judge)==0)
{
close(clientfd);
printf("连接已经被关闭!\n");
exit(0);
}
else
{
printf("我(Client)发送的消息:\n");
printf("*****");
printf("%s\n", msg);
if(send(clientfd, msg, 1024, 0) == -1)
{
perror("Send Failed");
continue;
}
}
}
}
return 0;
}