本文参考于:徐晓鑫女士的《后台开发》
(1)因为nagle算法。什么是nagle算法,简而言之是为了避免浪费资源,将包合并发送的算法。就好比你一个人打车必然不划算,可以约几个和你一道的人一起打车,宾主尽欢。但是随之而来的问题是,因为一起发送,故接收方不知道一个包的骑士==起始和截止位置。
(2)因为我们网络编程时,其实是将接受到的数据置入缓冲区,待应用层读取,但若应用层阻塞于其他事情而归期未至,则缓冲区的包又难以区分。
解决办法:封包,解包。
封包:读取包的长度,将之发送
解包:读取包,将之视为要发送包的长度,循环读取。
//package_ser.cpp
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<unistd.h>
#define Def_port 6666
using namespace std;
int MyRecv(int isock,char *msg,size_t count)
{
size_t tBytes=0;
int ithislen;
while(tBytes<count)
{
do{
ithislen=read(isock,msg,count-tBytes);
}while(ithislen<0&&errno==EINTR);
if(ithislen<0)
return (ithislen);
else if(ithislen==0)
return (tBytes);
tBytes+=ithislen;
msg+=ithislen;
}
}
int main(int argc,char **argv)
{
struct sockaddr_in ser,cli;
memset(&ser,0,sizeof(ser));
memset(&cli,0,sizeof(cli));
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
perror("sockfd");
return -1;
}
cout<<"crete sockfd success,is:"<<sockfd<<endl;
ser.sin_family=AF_INET;
ser.sin_port=htons(Def_port);
ser.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(sockfd,(struct sockaddr *)&ser,sizeof(struct sockaddr))==-1)
{
perror("bind");
return -1;
}
cout<<"bind success"<<endl;
if(listen(sockfd,10)==-1)
{
perror("listen");
return -1;
}
cout<<"listening......"<<endl;
socklen_t len=sizeof(ser);
char msg[10];
int acceptfd=accept(sockfd,(struct sockaddr *)&ser,&len);
if(acceptfd<0)
{
close(sockfd);
perror("accept");
return -1;
}
cout<<"accept success"<<endl;
ssize_t readlen=MyRecv(acceptfd,msg,sizeof(int));
if(readlen<0)
{
cout<<"read failured"<<endl;
return -1;
}
int slen=( int)ntohl(*( int*)msg);//这一句耐人寻味,将字符串的前int位读取并强制转换为int,即包长
cout<<"the length is:"<<slen<<endl;
readlen=MyRecv(acceptfd,msg,slen);
if(readlen<0)
{
cout<<"read failured"<<endl;
//perror("readlen");
//cout<<strerror(errno)<<endl;
return -1;
}
msg[slen]='\0';
cout<<msg<<endl;
close(acceptfd);
return 0;
}
//package_cli.cpp
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#define Def_port 6666
using namespace std;
int MySend(int isockfd,char *msg,size_t tlen)
{
int ithisSend;
unsigned int isend=0;
if(tlen==0)
return 0;
while(isend<tlen)
{ do{
ithisSend=send(isockfd,msg,tlen-isend,0);
}while(ithisSend<0&&errno==EINTR);
if(ithisSend<0)
return isend;
isend+=ithisSend;
msg+=ithisSend;
}
return tlen;
}
int main(int argc,char **argv)
{
struct sockaddr_in cli;
memset(&cli,0,sizeof(cli));
if(argc<2)
{ cout<<"Uasge: client [server ip address]\n";
return -1;
}
cli.sin_family=AF_INET;
cli.sin_port=htons(Def_port);
cli.sin_addr.s_addr=inet_addr(argv[1]);
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
perror("socket");
return -1;
}
cout<<"create sockfd success! is:"<<sockfd<<endl;
if(connect(sockfd,(struct sockaddr*)&cli,sizeof(cli))<0)
{
perror("connect");
return -1;
}
cout<<"connect success!"<<endl;
char *sendmsg="0123456789";
ssize_t wlen;
int len=strlen(sendmsg);
cout<<"the length is :"<<len<<endl;
int ilen=0;
char *buf=new char[100];
*(int*)(buf+ilen)=htonl(len);
ilen+=sizeof(int); //前int位置包长
memcpy(buf+ilen,sendmsg,len);
ilen+=len;
wlen=MySend(sockfd,buf,ilen);
if(wlen<0)
{
cout<<"send data failured"<<endl;
close(sockfd);
return 0;
}
cout<<"send data success,the length is"<<wlen<<" the data is:"<<sendmsg<<endl;
close(sockfd);
return 0;
}