1为什么出现沾包问题
TCP的底层有一个nagel算法 会将一定短的时间内的发往同一个接收端的 多个小的数据包组成一个整体 一起发送 而接收端无法区分数据的类型 所以就可能有冲突。
解决方法一:
是用sleep来将发送的东西分开,但是这样实际运用中并不靠谱并且实际传输中还有命令传输总不能发一个加一个sleep,而且sleep会是运行效率变慢所以这个方法实际开发中并不使用。
解决方法二:
就是将要发送的各种文件以及命令全部以结构体的方式打包发出,这样就不会出现沾包问题
2代码说明:
如果不存在,则回复给客户端文件不存在
如果存在,也要给客户端回复文件存在的消息
4.客户端收到服务的回复 如果不存在 则重新输入文件名
如果存在 则新建并打开文件 写|创建|清空
5.开始发送文件内容
6.文件结束发送 "****over*****" 给客户端
7.客户端收到 "****over****" 后就不要在recv 了
8.客户端退出 服务器等待下一次客户端的连接
3代码实现
客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <head.h>
//这里设置这个结构体主要是为了防止防沾包
#define N 512
typedef struct MSG_{
int num;
char buff[N];
}msg_t;
int jieshou_file(int sockfd,const char *filename)
{
int fd;
int ret=0;
msg_t tid;
if((fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666))==-1){
ERRLOG("open error3");
}
while(1){
memset(&tid,0,sizeof(tid));
if((ret=recv(sockfd,&tid,sizeof(tid),0))==-1){
ERRLOG("recv error");
}
if(!strncmp(tid.buff,"over",5)){
break;
}
write(fd,tid.buff,strlen(tid.buff));
}
return 0;
}
int main(int argc, char const *argv[])
{
int sockfd=0;
msg_t msg;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
ERRLOG("sockfd error");
}
struct sockaddr_in clientaddr;
clientaddr.sin_family=AF_INET;
clientaddr.sin_port=htons(atoi(argv[2]));
clientaddr.sin_addr.s_addr=inet_addr(argv[1]);
socklen_t clientaddr_len=sizeof(clientaddr);
if(connect(sockfd,(struct sockaddr*)&clientaddr,clientaddr_len)==-1){
ERRLOG("connect error");
}
printf("connect successfully\n");
while(1){
//输入你想要下载文件的姓名
memset(&msg,0,sizeof(msg));
printf("Please enter the file name you want to downloadn >");
fgets(msg.buff,sizeof(msg.buff),stdin);
msg.buff[strlen(msg.buff)-1]='\0';
//将文件名字发送给服务器
if(send(sockfd,&msg,sizeof(msg),0)==-1){
ERRLOG("Failed to send file name to server");
}
//接受服务器反回来的消息
memset(&msg,0,sizeof(msg));
if(recv(sockfd,&msg,sizeof(msg),0)==-1){
ERRLOG("recv error1");
}
if(msg.num==2){
printf("There is no such file as you typed\n");
continue;
}else{
jieshou_file(sockfd,msg.buff);
break;
}
}
close(sockfd);
return 0;
}
服务器
#include <head.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define N 512
typedef struct MSG_{
int num;
char buff[N];
}msg_t;
int send_file(int acceptfd,const char *filename)
{
int fd;
int ret=0;
msg_t tid;
if((fd=open(filename,O_RDONLY))==-1){
ERRLOG("open filename error");
}
while((ret=read(fd,tid.buff,sizeof(tid.buff)))>0){
tid.num=ret;
if(send(acceptfd,&tid,sizeof(tid),0)==-1){
ERRLOG("send error1");
}
memset(&tid,0,sizeof(tid));
}
memset(&tid,0,sizeof(tid));
strcat(tid.buff,"over");
if(send(acceptfd,&tid,sizeof(tid),0)==-1){
ERRLOG("send error2");
}
return 0;
}
int is_file(msg_t *msg)//判断文件是否存在
{
if(msg==NULL){
ERRLOG("The ginseng error");
}
int fd;
if((fd=open(msg->buff,O_RDONLY))==-1){
if(errno==2){
msg->num=2;
return -1;
}
ERRLOG("open file error");
}
return 0;
}
int main(int argc, char const *argv[])
{
int sockfd=0;
int acceptfd=0;
msg_t msg;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
ERRLOG("sockfd error");
}
//网络信息结构体
struct sockaddr_in serveraddr;
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
socklen_t serveraddr_len=sizeof(serveraddr);
//将网络信息结构体进行绑定
if(bind(sockfd,(struct sockaddr*)&serveraddr,serveraddr_len)==-1){
ERRLOG("bind error");
}
//将套接字设置为被动监听状态
if(listen(sockfd,5)==-1){
ERRLOG("listen error");
}
//设置一个新的网络信息结构体存储客户端信息
struct sockaddr_in clientaddr;
memset(&clientaddr,0,sizeof(clientaddr));
socklen_t clientaddr_len=sizeof(clientaddr);
if((acceptfd=accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len))==-1){
ERRLOG("accepfd error");
}
printf("客户[%s:%d]连接成功\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
while(1){
//接受客户端发来的文件名
memset(&msg,0,sizeof(msg));
if(recv(acceptfd,&msg,sizeof(msg),0)==-1){
ERRLOG("recv error3");
}
//验证是否有这个文件//当前目录
is_file(&msg);
//把是否有这个文件的信息发送给客户端
if(send(acceptfd,&msg,sizeof(msg),0)==-1){
ERRLOG("acceptfd error");
}
if(msg.num==2){
continue;
}else{
send_file(acceptfd,msg.buff);
}
}
close(acceptfd);
close(sockfd);
return 0;
}
4总结说明
该代码的要求是再文章的最后加一个over,如果不封装结构体,出现沾包问题,最后客户端收文件的时候他就找不到over,所以就会停不下了,用这个代码就是为了说明沾包问题,正常下载文件应该先向服务器获取对方的长度然后根据接受到的长度,让客户端停止接受。