/*
* visiopacket.h
*
* Created on: 2019年5月8日
* Author: hfeng.liu
*
* 粘包处理:由于TCP是基于流传输的机制,当发送多个间隔较小的小报文时,它会在缓冲区中缓存成一个报文发送给peer,这时peer
* 无法区分一个报文的边界。
* 解决的办法:让peer知道一个报文的边界,即知道它的长度。
*/
#include <string>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while(0)
#define handle_success(msg) \
do { printf(msg); exit(EXIT_SUCCESS); } while(0)
typedef struct message
{
uint32_t length;
char buf[1024];
}msg;
//readn函数
//说明:此函数解决了粘包的数据处理;
//@ssize_t:-1表示返回错误,0表示收到FIN信号,>0表示数据的长度
//@fd:文件描述符
//@buf:待写数据首地址
//@nByte:待读长度
ssize_t readn(int fd, void *buf, size_t nByte)
{
size_t nleft = nByte;
ssize_t nread = 0;
char * buf_p = (char*)buf;
while (nleft > 0)
{
nread = read(fd, buf_p, nleft);
if (nread<0 && errno==EINTR)
continue;
else if (nread == 0)
return 0;
else if (nread < 0)
return -1;
else
return nread;
nleft -= nread;
buf_p += nread;
}
return nByte;
}
//writen函数
//说明:此函数解决了缓冲区数据发送溢出的处理;
//@ssize_t:-1表示返回错误,0表示收到FIN信号,>0表示数据的长度
//@fd:文件描述符
//@buf:待写数据首地址
//@nByte:待写长度
ssize_t writen(int fd, void *buf, size_t nBytes)
{
size_t nleft = nBytes;
char *buf_p = (char*)buf;
int nwritten = 0;
while(nleft > 0)
{
nwritten = write(fd, buf_p, nleft);
if (nwritten<0 && errno==EINTR)
nwritten = 0;
else
return -1;
nleft -= nwritten;
buf_p += nwritten;
}
return nBytes;
}
//回射服务器客户端
#include <string>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include "visiopacket.h"
#define PORT 50001
#define ADDR ("127.0.0.1")
int main()
{
sockaddr_in s_sock, c_sock;
socklen_t sockLen = sizeof(sockaddr);
s_sock.sin_family = AF_INET;
s_sock.sin_port = htons(PORT);
inet_aton(ADDR, &s_sock.sin_addr);
//socket
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0) handle_error("socket()");
//connect
if (connect(sfd, (sockaddr*)&s_sock, sizeof(sockaddr)) < 0) handle_error("connect()");
if (getpeername(sfd, (sockaddr*)&c_sock, &sockLen) != 0) handle_error("getpeername()") ;
printf("peer sock info: %s:%d!\n", inet_ntoa(c_sock.sin_addr), ntohs(c_sock.sin_port));
msg send_msg;
msg rcv_msg;
while(fgets(send_msg.buf, sizeof(send_msg.buf), stdin) != NULL)
{
send_msg.length = strlen(send_msg.buf);
printf("length:%d\n", send_msg.length);
writen(sfd, &send_msg, send_msg.length + sizeof(send_msg.length));
read(sfd, &rcv_msg.length, sizeof(rcv_msg.length));
int ret = readn(sfd, rcv_msg.buf, rcv_msg.length);
if (ret < 0)
handle_error("read()");
else if (ret == 0)
break;
else
fputs(rcv_msg.buf, stdout);
memset(&rcv_msg, 0, sizeof(rcv_msg));
memset(&send_msg, 0, sizeof(send_msg));
}
close(sfd);
return 0;
}
//回射服务器服务端
#include <string>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include "visiopacket.h"
/*
* TIME_WAIT触发:在与客户端建立TCP连接后,关闭服务侧,服务器会TIME_WAIT的状态,2个MSL时间后,才会释放掉连接。
* 解决服务重启后,不需要等待TIME_WAIT的问题:
* int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
*
*/
/*
* 僵尸进程:客户端关闭后,服务侧的子进程没有关闭 “63753 pts/19 00:00:00 refSrv <defunct>”;
* 解决方法1:使用signal(SIGCHLD, SI_IGN)函数忽略僵尸进程;
* 解决方法2:
*/
#define PORT 50001
#define ADDR ("127.0.0.1")
int main()
{
signal(SIGCHLD, SIG_IGN);
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0) handle_error("socket()");
sockaddr_in s_sock, c_sock;
socklen_t socklen = sizeof(sockaddr);
s_sock.sin_family = AF_INET;
s_sock.sin_port = htons(PORT);
//inet_aton(ADDR, &s_sock.sin_addr);
s_sock.sin_addr.s_addr = htonl(INADDR_ANY);
int on = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) handle_error("setsockopt()");
if (bind(sfd, (sockaddr *)&s_sock, sizeof(sockaddr)) < 0) handle_error("bind()");
if (listen(sfd, 5) < 0) handle_error("listen()");
while (true)
{
int afd = accept(sfd, (sockaddr*)&c_sock, &socklen);
printf("Client(%s:%d) connected!\n", inet_ntoa(c_sock.sin_addr), ntohs(c_sock.sin_port));
pid_t pid = fork();
if (pid < 0)
handle_error("fork()");
else if (pid == 0)
{
close(sfd);
msg rcvMsg;
while (true)
{
memset(&rcvMsg, 0, sizeof(rcvMsg));
read(afd, &rcvMsg.length, sizeof(rcvMsg.length));
int ret = readn(afd, rcvMsg.buf, rcvMsg.length);
if (ret < 0)
handle_error("read()");
//else if (ret == 0)
//break;
else
{
printf("Receive length = %d\n", rcvMsg.length);
fputs(rcvMsg.buf, stdout);
writen(afd, &rcvMsg, rcvMsg.length + sizeof(rcvMsg.length));
}
}
handle_success("client close!\n");
}
close(afd);
}
close(sfd);
return 0;
}