socket编程(3-3)
接下来,我们再来实现一个点对点聊天程序。

对等通信,双发都可以发送数据。A可以发数据给B,B也可以发数据给A,通过双方共同维护一个套接字来实现。A维护connA,B维护connB。但是有一个问题,A在接收B发过来的数据的时候(read的时候),他怎么去获得键盘上的输入呢?就我们现在的技术而言,我们只能去在创建一个进程,一个进程用来接收对等方发来的数据,一个进程用来获取键盘输入。对B来说也是一样,也需要有两个进程。所以我们要用多进程的方式来实现。
这个时候,我们实现点对点通信,就不考虑,服务器实现与多个客户端通信的情况了。只考虑一个服务器端与一个客户端通信的情况。
我们假设父进程专门用来获取对方的数据;不需要回射回去
子进程用来发送数据,通过从标准输入获取一行数据,然后把它发送出去(通过conn吧它发送出去)。
就是这样不断的循环,跳出循环我们就把他退出。
服务器端代码:
#include<stdio.h> #include <sys/socket.h> #include<sys/types.h> #include<unistd.h> #include<signal.h> #include <netinet/in.h> #include <arpa/inet.h> #include<stdlib.h> #include<errno.h> #include<string.h> void handler(int sig) { printf("recv a sig = %d\n",sig); exit(EXIT_SUCCESS); } int main() { /*create a socket*/ int sockfd; if((sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) printf("socket err"); /*init addr*/ struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/ /*inet_aton("127.0.0.1",&servaddr.sin_addr);*/ int on = 1;/*开启地址重复利用*/ if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0) printf("setsockopt err"); /*将创建的套接字与本地地址进行绑定*/ if(bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0) printf("blind err"); /*监听套接字*/ if (listen(sockfd,SOMAXCONN) < 0 ) printf("listen err:"); struct sockaddr_in peeraddr; socklen_t peerlen = sizeof(peeraddr); int conn; /*主动套接字*/ if ((conn = accept(sockfd,(struct sockaddr*)&peeraddr,&peerlen)) < 0 ) printf("accept err"); printf("ip = %s,port = %d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port)); pid_t pid; pid = fork();//创建一个子进程 if(pid == -1) printf("fork err"); if (pid == 0 )//子进程,发送数据 { signal(SIGUSR1,handler);//接收信号 /*发送数据的进程*/ char sendbuf[1024] = {0}; while (fgets(sendbuf,sizeof(sendbuf),stdin) != NULL) { write(conn,sendbuf,strlen(sendbuf)); memset(sendbuf,0,sizeof(sendbuf)); } printf("child close\n"); exit(EXIT_SUCCESS); } else//父进程,接收数据 { /*接收数据的进程*/ char recvbuf[1024]; while(1) { memset(recvbuf,0,sizeof(recvbuf)); int ret = read(conn,recvbuf,sizeof(recvbuf)); if (ret == -1)/*读取错误*/ printf("read err"); else if(ret == 0) { printf("peer close\n");/*对方关闭*/ break; } fputs(recvbuf,stdout); } printf("parent close\n"); kill(pid,SIGUSR1);/*向子进程发送一个信号*/ exit(EXIT_SUCCESS); } return 0; }
客户端代码:
#include<stdio.h> #include <sys/socket.h> #include<sys/types.h> #include<unistd.h> #include<signal.h> #include <netinet/in.h> #include <arpa/inet.h> #include<stdlib.h> #include<errno.h> #include<string.h> void handler(int sig) { printf("recv a sig = %d\n",sig); exit(EXIT_SUCCESS); } int main() { /*创建套接字*/ int sockfd; if((sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) printf("socket err"); /*serv addr and port*/ struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); /*连接,一旦连接成功,sockfd套接字就处于已连接状态,从逻辑上 是与服务器端的conn套接字连接*/ if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0) printf("connect err"); pid_t pid; pid = fork();//创建一个子进程 if (pid == -1) printf("fork err"); if (pid == 0)//子进程,发送数据 { signal(SIGUSR1,handler);//接收信号 char sendbuf[1024] = {0}; while (fgets(sendbuf,sizeof(sendbuf),stdin) != NULL) { write(sockfd,sendbuf,sizeof(sendbuf)); memset(sendbuf,0,sizeof(sendbuf)); } close(sockfd); } else//父进程,接收数据 { char recvbuf[1024]={0}; while (1) { memset(recvbuf,0,sizeof(recvbuf)); int ret= read(sockfd,recvbuf,sizeof(recvbuf)); if (ret == -1) printf("read err"); else if(ret == 0) { printf("peer close \n"); break; } fputs(recvbuf,stdout); } close(sockfd); kill(pid,SIGUSR1);//向子进程发送数据 exit(EXIT_SUCCESS); } return 0; }
父进程退出,子进程是没有退出的。怎么解决呢?我们让父进程退出时,通知到子进程,这可以通过一个方式来实现---信号。输入kill -l 我们来借助一个信号SIGUSR1(用户自定义信号)
父进程向子进程发送信号 kill()函数 ,可以通过man 2 kill 查看
当子进程收到父进程发出的信号,就会执行handler函数。
我们这里实现了使用信号来实现进程间通信。

浙公网安备 33010602011771号