3.Linux网络编程-粘包处理

/*
 * 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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值