Server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#define ERR_EXIT(m)\
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
struct packet
{
int len; //包头 存放包体中数据的实际长度
char buf[1024];
};
/**
进程由于系统调用或者中断进入内核
在从内核空间返回到用户空间之前,
检查是否有信号产生,跳到用户态执行信号处理函数
linux 信号处理机制,请见另篇博文的浅析
信号产生的时候 ,不会立即响应,这是和中断不一样的一个地方
*/
void handler(int sig)
{
exit(0);
}
ssize_t readn(int fd,void * buf,size_t count)
{
size_t nleft = count;
ssize_t nread;
char * bufp = (char *)buf;
while(nleft > 0)
{
nread = read(fd,bufp,nleft);
if (nread == -1)
{
/**
EINVAL 传给系统调用的参数不正确
EINTR 被信号中断
EFAULT 参数中有一指针指向无法存取的内存空间
信号会打断阻塞的系统调用。因此这里要判断一下。
*/
if (errno == EINTR)//被信号中断
{
continue;
}else if (errno == EFAULT)//参数中有一指针指向无法存取的内存空间 会报"bad address"错误
{
printf("参数有错误\n");
}
return -1;
}else if (nread == 0) //对方关闭了连接
{
return count - nleft;
}else{
bufp += nread;
nleft -= nread; //剩余的字节数,如果大于0,继续读取
}
}
return count;
}
ssize_t writen(int fd, void * buf,size_t count)
{
size_t nleft = count;
ssize_t nwrite ;
char * bufp = (char *)buf;
while(nleft > 0)
{
if((nwrite = write(fd,bufp,nleft)) < 0)
{
if (errno == EINTR)
{
continue;
}else{
return -1;
}
}else if (nwrite == 0)
{
continue;
}else{
bufp += nwrite;
nleft -= nwrite;
}
}
return count;
}
void do_server(int conn)
{
struct packet recvbuf;
int n;
while(1)
{
memset(&recvbuf,0,sizeof(recvbuf));
/**
先接收包头
*/
int ret = readn(conn,&recvbuf.len,4);
printf("packet header =%d \n",ret );
if (ret == -1)
{
ERR_EXIT("read");
}else if (ret < 4)
{
printf("client close\n");
break;
}
/**
接收包体
*/
n = ntohl(recvbuf.len);
ret = readn(conn,&recvbuf.buf,n);
if (ret == -1)
{
ERR_EXIT("read");
}else if (ret < n)
{
printf("client close\n");
break;
}
fputs(recvbuf.buf,stdout);
}
}
int main(int argc, char const *argv[])
{
int listenfd;
int ret;
int conn;
pid_t pid;
listenfd = socket(AF_INET,SOCK_STREAM,0);
if (listenfd == -1)
{
ERR_EXIT("socket");
}
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666); //主机字节序转换成网络字节序
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
int on = 1;
ret = setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
if (ret == -1)
{
ERR_EXIT("setsockopt");
}
ret = bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if (ret == -1)
{
ERR_EXIT("bind");
}
//SOMAXCONN = 三次握手已完成 + 三次握手为完成
ret = listen(listenfd,SOMAXCONN);
if (ret == -1)
{
ERR_EXIT("listen");
}
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
conn = accept(listenfd,(struct sockaddr *)&clientaddr,&len);
if (conn == -1)
{
ERR_EXIT("accept");
}
pid = fork();
if (pid == 0) //子进程
{
//标准C 写法
//signal(SIGUSR1,handler);
struct sigaction act;
act.sa_handler = handler;
//将阻塞信号集全部置 0
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
/**
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
signum : 你要为那个信号注册捕捉函数 : SIGKILL、SIGSTOP 除外(这两个不能被捕捉、阻塞、忽略).
act : 新设定的动作
oldact : 信号原来的动作.输出参数
返回值 : 0 成功
-1 失败
struct sigaction
{
早期的函数 与下面的一个函数指针互斥
通过sa_mask来选择采用那种捕捉函数
void (*sa_handler)(int);
void (*sa_sigaction)(int ,siginfo_t *,void *);
临时信号屏蔽字:
在执行捕捉函数的时候,设置阻塞其他信号,
退出捕捉函数后,还原原有的阻塞信号集
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
*/
int retval = sigaction(SIGUSR1,&act,NULL);
if (retval == -1)
{
ERR_EXIT("sigaction");
}
int n;
struct packet sendbuf;
memset(&sendbuf,0,sizeof(sendbuf));
while(fgets(sendbuf.buf,sizeof(sendbuf.buf),stdin) != NULL)
{
n = strlen(sendbuf.buf);
sendbuf.len = htonl(n);
writen(conn,&sendbuf,4 + n);
memset(&sendbuf,0,sizeof(sendbuf));
}
exit(0);
}else if (pid > 0)
{
while(1)
{
printf("---do_server\n");
do_server(conn);
break;
}
/**
向子进程发信号
*/
kill(pid,SIGUSR1);
exit(0);
}else{
ERR_EXIT("fork");
}
close(conn);
close(listenfd);
return 0;
}
Client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#define ERR_EXIT(m)\
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
struct packet
{
int len; //包头 存放包体中数据的实际长度
char buf[1024];
};
void handler(int sig)
{
exit(0);
}
ssize_t readn(int fd,void * buf,size_t count)
{
size_t nleft = count;
ssize_t nread;
char * bufp = (char *)buf;
while(nleft > 0)
{
nread = read(fd,bufp,nleft);
if (nread == -1)
{
/**
EINVAL 传给系统调用的参数不正确
EINTR 被信号中断
EFAULT 参数中有一指针指向无法存取的内存空间
*/
if (errno == EINTR)//被信号中断
{
continue;
}else if (errno == EFAULT)//参数中有一指针指向无法存取的内存空间 会报"bad address"错误
{
printf("参数有错误\n");
}
return -1;
}else if (nread == 0) //对方关闭了连接
{
return count - nleft;
}else{
bufp += nread;
nleft -= nread; //剩余的字节数,如果大于0,继续读取
}
}
return count;
}
ssize_t writen(int fd, void * buf,size_t count)
{
size_t nleft = count;
ssize_t nwrite ;
char * bufp = (char *)buf;
while(nleft > 0)
{
if((nwrite = write(fd,bufp,nleft)) < 0)
{
if (errno == EINTR)
{
continue;
}else{
return -1;
}
}else if (nwrite == 0)
{
continue;
}else{
bufp += nwrite;
nleft -= nwrite;
}
}
return count;
}
void do_server(int conn)
{
struct packet recvbuf;
int n;
while(1)
{
memset(&recvbuf,0,sizeof(recvbuf));
/**
先接收包头
*/
int ret = readn(conn,&recvbuf.len,4);
if (ret == -1)
{
ERR_EXIT("read");
}else if (ret < 4)
{
printf("client close\n");
break;
}
/**
接收包体
*/
n = ntohl(recvbuf.len);
ret = readn(conn,&recvbuf.buf,n);
if (ret == -1)
{
ERR_EXIT("read");
}else if (ret < n)
{
printf("client close\n");
break;
}
fputs(recvbuf.buf,stdout);
}
}
int main(int argc, char const *argv[])
{
int sock;
int ret;
pid_t pid;
sock = socket(AF_INET,SOCK_STREAM,0);
if (sock == -1)
{
ERR_EXIT("socket");
}
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
ret = connect(sock,(struct sockaddr *)&servaddr,sizeof(servaddr));
if (ret == -1)
{
ERR_EXIT("connect");
}
pid = fork();
if (pid == 0)
{
//标准C 写法
//signal(SIGUSR1,handler);
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGUSR1,&act,NULL);
int n;
struct packet recvbuf;
memset(&recvbuf,0,sizeof(recvbuf));
while(fgets(recvbuf.buf,sizeof(recvbuf.buf),stdin) != NULL)
{
n = strlen(recvbuf.buf);
recvbuf.len = htonl(n);
writen(sock,&recvbuf,4 + n);
memset(&recvbuf,0,sizeof(recvbuf));
}
exit(0);
}else if (pid > 0)
{
struct packet recvbuf;
memset(&recvbuf,0,sizeof(recvbuf));
while(1)
{
do_server(sock);
break;
}
/**
向子进程发送信号
*/
kill(pid,SIGUSR1);
exit(0);
}else{
ERR_EXIT("fork");
}
close(sock);
return 0;
}