用readn与written实现解决粘包问题
来源:互联网 发布:bluecloud 新域名 编辑:程序博客网 时间:2024/06/05 16:56
使用TCP流式套接字,TCP粘包是指发送方发送的若干包数据到接收方接
收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接
收方造成。发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,
发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数
据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,
这样接收方就收到了粘包数据。出现这种情况的原因是TCP协议采用了著名
的nagle算法,这种算法的策略就是把尽量把小的数据包拼接起来发送,目的
是减少发送方的系统开销。
接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘
包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从
该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则
下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据
预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数
据。
如果网络速率较快的话,建议作一个高效的协议,从接收端把数据划分
开来,这是比较普遍的做法。但是如果网络本身较慢,可以关闭nagle算法。
关于协议的问题以后再讨论,这里介绍关闭nagle算法的方法:
SOCKET sock;
const int bNodelay = 1;
sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
setsockopt(sock ,IPPROTO_TCP,TCP_NODELAY,(char *)&bNodelay,sizeof(bNodelay));
echocli.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
struct packet
{
int len;
char buf[1024];
};
ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf;
while (nleft > 0)
{
if ((nread = read(fd, bufp, nleft)) < 0)
{
if (errno == EINTR)
continue;
return -1;
}
else if (nread == 0)
return count - nleft;
bufp += nread;
nleft -= nread;
}
return count;
}
ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf;
while (nleft > 0)
{
if ((nwritten = write(fd, bufp, nleft)) < 0)
{
if (errno == EINTR)
continue;
return -1;
}
else if (nwritten == 0)
continue;
bufp += nwritten;
nleft -= nwritten;
}
return count;
}
int main(void)
{
int sock;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
ERR_EXIT("socket");
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");
if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("connect");
struct packet sendbuf;
struct packet recvbuf;
memset(&sendbuf, 0, sizeof(sendbuf));
memset(&recvbuf, 0, sizeof(recvbuf));
int n;
while (fgets(sendbuf.buf, sizeof(sendbuf.buf), stdin) != NULL)
{
n = strlen(sendbuf.buf);
sendbuf.len = htonl(n);
writen(sock, &sendbuf, 4+n);
int ret = readn(sock, &recvbuf.len, 4);
if (ret == -1)
ERR_EXIT("read");
else if (ret < 4)
{
printf("client close\n");
break;
}
n = ntohl(recvbuf.len);
ret = readn(sock, recvbuf.buf, n);
if (ret == -1)
ERR_EXIT("read");
else if (ret < n)
{
printf("client close\n");
break;
}
fputs(recvbuf.buf, stdout);
memset(&sendbuf, 0, sizeof(sendbuf));
memset(&recvbuf, 0, sizeof(recvbuf));
}
close(sock);
return 0;
}
echosrv.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
struct packet
{
int len;
char buf[1024];
};
ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf;
while (nleft > 0)
{
if ((nread = read(fd, bufp, nleft)) < 0)
{
if (errno == EINTR)
continue;
return -1;
}
else if (nread == 0)
return count - nleft;
bufp += nread;
nleft -= nread;
}
return count;
}
ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf;
while (nleft > 0)
{
if ((nwritten = write(fd, bufp, nleft)) < 0)
{
if (errno == EINTR)
continue;
return -1;
}
else if (nwritten == 0)
continue;
bufp += nwritten;
nleft -= nwritten;
}
return count;
}
void do_service(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);
writen(conn, &recvbuf, 4+n);
}
}
int main(void)
{
int listenfd;
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
/* if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
ERR_EXIT("socket");
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(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
ERR_EXIT("setsockopt");
if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("bind");
if (listen(listenfd, SOMAXCONN) < 0)
ERR_EXIT("listen");
struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int conn;
pid_t pid;
while (1)
{
if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
ERR_EXIT("accept");
printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
pid = fork();
if (pid == -1)
ERR_EXIT("fork");
if (pid == 0)
{
close(listenfd);
do_service(conn);
exit(EXIT_SUCCESS);
}
else
close(conn);
}
return 0;
}
makefile:
.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
BIN=echosrv echocli
all:$(BIN)
%.o:%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o $(BIN)
- 用readn与written实现解决粘包问题
- 用readn与written实现解决粘包问题
- 流协议与粘包,粘包差生的原因,粘包处理方案,4.readn/writen 的封装
- readn、readline与writen
- 解决粘包问题
- Mina学习(四):实现自定义编解码器并解决半包,丢包,粘包问题
- 解决内存不可read和written的问题
- Socket编程实践(5) --TCP粘包问题与解决
- TCP粘包问题的分析与解决
- Socket编程实践(5) --TCP粘包问题与解决
- Socket编程实践(5) --TCP粘包问题与解决
- unix中readn()与readline 的作用,,write,,read,,readn
- netty解决TCP网络传输中的拆包与粘包问题
- readn和writen函数实现通信
- TCP粘包与拆包问题
- Java NIO 实现进程通讯,解决用户自定义数据的组包和拆分粘包的问题
- Android Socket开源库实现socket长连接,解决socket粘包问题.
- 解决TCP网络传输“粘包”问题
- Linux/error.h中的错误对应(include/asm-generic/(errno.h)、(errno-base.h))
- [LeetCode] Implement strStr()
- Apex系统自带的函数
- 美橙互联主机评测
- NSTimer导致的内存泄露,找了好久的问题
- 用readn与written实现解决粘包问题
- Maven的安装、配置及使用入门
- Linux系统维护监控工具集sysstat详解
- android 通过UncaughtExceptionHandler处理和上传错误日志
- php正则匹配文章中的远程图片地址并下载图片到本地
- 零基础学习hadoop到上手工作线路指导(编程篇)
- 嵌入式系统中看门狗的使用总结
- WINDOW 安装pear
- php中正则表达式基本语法