Linux 网络编程——TCP多进程聊天程序
实现目标
【1】创建TCP服务器和客户端,实现简易聊天程序;
【2】数据收发单独进程实现;
【3】客户端/服务器任一结束,结束连接和对方进程。
服务器端(server)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <arpa/inet.h>
#include <errno.h>
#include <resolv.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
void sig_parent_handle(int signo)
{
printf("receive process child signal\n");
if(waitpid(-1, NULL, 0) < 0)
{
perror("waitpid error:");
}
exit(0);
}
void sig_child_handle(int signo)
{
if (signo == SIGHUP)
{
printf("receive process parent SIGHUP signal\n");
exit(0);
}
}
int main (int argc, char * argv[])
{
int s_fd, c_fd, pid;
socklen_t addr_len;
struct sockaddr_in s_addr, c_addr;
char buf[1024];
ssize_t size = 0;
if(argc != 3)
{
printf("format error!\n");
printf("usage: server <address> <port>\n");
exit(1);
}
/* 进程通知信号 */
signal(SIGCHLD,sig_parent_handle);
/* 服务器端地址 */
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
if(!inet_aton(argv[1], (struct in_addr *) &s_addr.sin_addr.s_addr))
{
perror("invalid ip addr:");
exit(1);
}
/* 创建socket */
if((s_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket create failed:");
exit(1);
}
/* 端口重用,调用close(socket)一般不会立即关闭socket,而经历“TIME_WAIT”的过程 */
int reuse = 0x01;
if(setsockopt(s_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(int)) < 0)
{
perror("setsockopt error");
close(s_fd);
exit(1);
}
/* 绑定地址 */
if(bind(s_fd, (struct sockaddr*)&s_addr, sizeof(s_addr)) < 0)
{
perror("bind error");
close(s_fd);
exit(1);
}
/* 监听socket */
if(listen(s_fd, 5) < 0)
{
perror("listen error");
close(s_fd);
exit(1);
}
addr_len = sizeof(struct sockaddr);
c_fd = accept(s_fd, (struct sockaddr*)&c_addr, (socklen_t *)&addr_len);
if(c_fd < 0)
{
perror("accept error");
close(s_fd);
exit(1);
}
else
{
printf("connected with ip: %s and port: %d\n", inet_ntop(AF_INET,&c_addr.sin_addr, buf, 1024), ntohs(c_addr.sin_port));
}
printf("waiting client connecting\r\n");
/* 创建进程 */
pid = fork();
if(pid < 0)
{
perror("fork error");
exit(1);
}
if(pid == 0) /* 子进程发送消息 */
{
signal(SIGHUP, sig_child_handle);
prctl(PR_SET_PDEATHSIG, SIGHUP);
for(; ;)
{
printf("please enter message to send:\n");
fflush(stdout);
memset(buf, 0, sizeof(buf));
size = read(STDIN_FILENO, buf, sizeof(buf) - 1);
if(size > 0)
{
buf[size - 1] = '\0';
}
else if(size == 0)
{
printf("read is done...\n");
break;
}
else
{
perror("read stdin error");
break;
}
if(!strncmp(buf, "quit", 4))
{
printf("close the connect!\n");
break;
}
if(buf[0] == '\0')
{
continue;
}
size = write(c_fd, buf, strlen(buf));
if(size <= 0)
{
printf("message'%s' send failed!errno code is %d,errno message is '%s'\n",buf, errno, strerror(errno));
break;
}
}
}
else /* 父进程接收消息 */
{
for(;;)
{
memset(buf, 0, sizeof(buf));
size = read(c_fd, buf, sizeof(buf) - 1);
if(size > 0)
{
printf("message recv %dByte: \n%s\n",size,buf);
}
else if(size < 0)
{
printf("recv failed!errno code is %d,errno message is '%s'\n",errno, strerror(errno));
break;
}
else
{
printf("client disconnect!\n");
break;
}
}
}
close(s_fd);
close(c_fd);
return 0;
}
客户端(client)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/prctl.h>
void sig_parent_handle(int sig)
{
printf("receive process child signal\n");
if(waitpid(-1, NULL, 0) < 0)
{
perror("waitpid error:");
}
exit(0);
}
void sig_child_handle(int signo)
{
if (signo == SIGHUP)
{
printf("receive process parent SIGHUP signal\n");
exit(0);
}
}
int main (int argc, char * argv[])
{
int c_fd,pid;
int ret;
struct sockaddr_in s_addr;
socklen_t addr_len;
char buf[1024];
ssize_t size;
if(argc != 3)
{
printf("format error!\n");
printf("usage: client <address> <port>\n");
exit(1);
}
/* 进程通知信号 */
signal(SIGCHLD,sig_parent_handle);
/* 创建socket */
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if(c_fd < 0)
{
perror("socket create failed");
return -1;
}
/* 服务器端地址 */
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
if(!inet_aton(argv[1], (struct in_addr *) &s_addr.sin_addr.s_addr))
{
perror("invalid ip addr");
exit(1);
}
/* 连接服务器*/
addr_len = sizeof(s_addr);
ret = connect(c_fd, (struct sockaddr*)&s_addr, addr_len);
if(ret < 0)
{
perror("connect server failed");
exit(1);
}
/* 创建进程 */
pid = fork();
if(pid < 0)
{
perror("fork error");
exit(1);
}
if(pid == 0) /* 子进程发送消息 */
{
signal(SIGHUP, sig_child_handle);
prctl(PR_SET_PDEATHSIG, SIGHUP);
for(;;)
{
memset(buf, 0, sizeof(buf));
printf("please enter message to send:\n");
fflush(stdout);
memset(buf, 0, sizeof(buf));
size = read(STDIN_FILENO, buf, sizeof(buf) - 1); /* 从终端读取输入信息 */
if(size > 0)
{
buf[size - 1] = '\0';
}
else if(size == 0)
{
printf("read is done...\n");
break;
}
else
{
perror("read stdin error");
break;
}
if(!strncmp(buf, "quit", 4))
{
printf("close the connect!\n");
break;
}
if(buf[0] == '\0')
{
continue;
}
size = write(c_fd, buf, strlen(buf));
if(size <= 0)
{
printf("message'%s' send failed!errno code is %d,errno message is '%s'\n",buf, errno, strerror(errno));
break;
}
}
}
else /* 父进程接收消息 */
{
for(;;)
{
size = read(c_fd, buf, sizeof(buf)); /* 读取服务器消息 */
if(size > 0)
{
printf("message recv %dByte: \n%s\n",size,buf);
}
else if(size < 0)
{
printf("recv failed!errno code is %d,errno message is '%s'\n",errno, strerror(errno));
break;
}
else
{
printf("server disconnect!\n");
break;
}
}
}
close(c_fd);
return 0;
}
执行效果