服务端:socket->address->bind->listen->loop{accpet->read->write->close(auto socket)}->close(listen socket)
1)因为是阻塞模式,所以客户端没有关闭的时候,服务端会阻塞在read函数,
2)客户端发送之后断开连接。是一个有序的数据片。服务端由内核经过排序片。一定会接收到数据。
而不会因为close的数据先到就以为没有数据。所以必须理解客户端的行为。也就是write和close函数。
他们都是发送数据包。是有顺序的,seq的。包括connect也是发送数据包。只不过工作由内核处理。
connect,write,close。如果按顺序写。发送的包的seq number是持续增加的。
客户端:socket->connect->wirte->loop(read)->close(client socket)
1)因为是阻塞模式。服务端的read会阻塞。所以客户端write之后再read。服务端是一定会接收到数据,再进行下一步发送数据的。
2)而客户端也是阻塞模式。所以read是一定会等待服务端发来的数据。并等到服务端的close信息。再完成接收数据步骤。
知识点:
char a[]=“” 会计算字符大小并加入结束符
read 函数,会读入任何符号,包括结束符号。
snprintf(&buff,n,"",x),实际上的字符大小只有n-1.会自动加入结束符号。
strlen 来计算实际的字符数量。不会包括结束符号。
1)所以write的时候用strlen确定实际字符大小。
2)read的时候。用一个n大小的char数组接收,并明确read的大小为n-1.方便放满字符还可以自行加入结束符号。
3)每次读之前,必须清空char数组,方便下次写入。
未处理知识点:
1)因为是阻塞模式。服务端没法确定客户端不再发送数据。因为客户端不可能关闭。它还要等待接收数据。
而客户端不关闭。服务端,会一直阻塞在read函数。
所以本实验是,客户固定发送比较小的数据,服务端用较大的缓冲区一次接收完客户数据。
而不考虑服务端一次接收不完的情况。
是否可以自己加入结束边界标志?对。书上好像提到过。应该是这样处理。
2)如果服务端一次没有读完客户数据。socket里面还有数据。这个时候发送,是发送正常的?是因为socket有2个缓冲区还是,之前的缓冲区清空了?
server:
#include <iostream> #include <sys/socket.h>//{socket_type.h}:socket,AF_INET,SOCK_STREAM,PF_INET #include <netdb.h>//{<netinet/in.h>}:IPPROTO_TCP #include <sys/errno.h> #include <string.h> #include <stdio.h>//perror #include <fcntl.h> #include <unistd.h>//close. #include <time.h> #include <thread> #include <arpa/inet.h>//INET_PTON using namespace std; typedef struct sockaddr_in SA; void Accpetthread(int serverFD); int main() { //socket->addr->bind->listen->accept(read ,write) int serverFD; int intflag; SA serverAddr; bzero(&serverAddr,sizeof(serverAddr)); serverFD=socket(AF_INET,SOCK_STREAM,IPPROTO_IP); if(serverFD==-1) { perror("create()"); return -1; } //serverAddr.sin_addr.s_addr=htonl(INADDR_ANY); inet_pton(AF_INET,"127.0.0.1",&serverAddr.sin_addr); serverAddr.sin_family=AF_INET; serverAddr.sin_port=htons(3004); //serverAddr.sin_zero?? intflag=bind(serverFD,(sockaddr*)&serverAddr,sizeof(sockaddr)); if(intflag==-1) { perror("bind()"); return -1; } listen(serverFD,10);//max queue? thread a=thread(Accpetthread,serverFD); a.detach(); int cmd; cout<<"exist:input 88"<<endl; for(;;) { cin>>cmd; if(cmd==88) { break; } } close(serverFD);//close 之后,地址不能使用.要等待2ml return 0; } // void Accpetthread(int serverFD) { for(;;) { int serverTempFD=accept(serverFD,0,0); //read char readbuf[51]; int sizeread= read(serverTempFD,readbuf,50); readbuf[sizeread]=‘\0‘;//以免溢出,插入结束符号. char writebuff[100]; bzero(writebuff,100);//以免溢出,全部写结束符号. int tempsize= snprintf(writebuff,99,"%s:%d",readbuf,strlen(readbuf)); cout<<writebuff<<endl; cout<<strlen(writebuff)<<endl;//为什么size是100?不是根绝\0决定的吗? write(serverTempFD,writebuff,strlen(writebuff)); close(serverTempFD); } }
client:
#include <iostream> #include <sys/socket.h>//{socket_type.h}:socket,AF_INET,SOCK_STREAM,PF_INET #include <netdb.h>//{<netinet/in.h>}:IPPROTO_TCP #include <sys/errno.h> #include <string.h> #include <stdio.h>//perror #include <fcntl.h> #include <unistd.h>//close. #include <time.h> #include <netinet/in.h> #include<arpa/inet.h>//INET_PTON using namespace std; int main() { //socket->connect->read. int socketClientFD; int statusFlag; socketClientFD=socket(PF_INET,SOCK_STREAM,IPPROTO_IP); if(socketClientFD==-1) { perror("socket()"); return -1; } struct sockaddr_in serverAddr; bzero(&serverAddr,sizeof(serverAddr)); serverAddr.sin_family=AF_INET; inet_pton(AF_INET,"127.0.0.1",&serverAddr.sin_addr); //printf("%0x,%0x,%0x,%0x",((char*)&serverAddr.sin_addr)[0],((char*)&serverAddr.sin_addr)[1],((char*)&serverAddr.sin_addr)[2],((char*)&serverAddr.sin_addr)[3]); serverAddr.sin_port=htons(3004); statusFlag=connect(socketClientFD,(sockaddr*)&serverAddr,sizeof(serverAddr)); if(statusFlag==-1) { perror("connect()"); return -1; } char buff[11]; char writeChar[16]="hi,i am client.";//一定会加入结束符号.char 大小要比实际字符大1. statusFlag= write(socketClientFD,writeChar,strlen(writeChar));//写的时候只发送字符。不发送结束符。所以用strlen. if(statusFlag==-1) { perror("write()"); return -1; } for(;;) { //bzero(buff,11);//每次要清空,更安全点。虽然后面已经buff[statusFlag]=‘\0‘; statusFlag=read(socketClientFD,buff,10);//这里会读入任何符号。包括结束符号。所以要求写的时候,不要发送结束符号。因此要求用strlen,确定实际字符。 if(statusFlag>0) { buff[statusFlag]=‘\0‘; cout<<buff<<flush; } else if(statusFlag==0) { cout<<endl; break; } else { perror("read()"); return -1; } } cout<<"eixist:input 88."<<endl; int cmd; while(1) { cin>>cmd; if(cmd==88) { close(socketClientFD); break; } } return 0; }