我们天天用QQ传送文件,也习惯了用飞鸽传送文件。但里边的具体实现是怎么样的呢?其实,过程很多简单,相信大家都知道,我这里呢,就多此一举给大家详细分析分析,做个总结,也为网络程序的编写搭一个整体的框架。好了,现在开始,我会分为两个端来说明:客户端和服务器端。今天先说客户端,下次再说服务器端。
直接上源码,来的简单实用且讲解不费劲,文中的代码是完整的代码,行文中蓝色部分是说明部分,使用时去掉及可以了,现在开始:
这一段也应该没有问题,注释中已经说的很明白了。一旦在上面建立了连接,这时就要告诉服务器我要接受你的啥文件(file_name),然后本地创建一个文件以便准备接收服务器传过来的东西。
这一段是关键,我刚才说了,既然在本地套接口client_socket和服务器信息套接口地址上建立了连接,那么和服务器的所有通信就丢给了client_socket.一旦服务器有信息要写回给客户端,客户端的接口就是client_socket,r,然后我们只要把这里边的信息写入buffer,然后把buffer里的信息写入本地文件的文件描述符就好了(fd).最后说明的是我们用了循环,是因为谁也不能拍着胸脯说,能一下子接收完,网络的能力还是有限的,那就循环判断当服务器发过来的信息长度为0时,我就认为是传输结束了。当所有的一切都结束了,好那就关闭以前建立的连接啦,文件描述符啦,套接口啦:
代码讲完了,其实相当简单,刚都说了,我是多此一举来总结下大家都知道,最后给你一个流程图:
直接上源码,来的简单实用且讲解不费劲,文中的代码是完整的代码,行文中蓝色部分是说明部分,使用时去掉及可以了,现在开始:
作为C语言,当然是先来个main()了:
int main(int argc,char**argv)
{
if(argc !=2) //输入命令提示信息
{
printf("Please enter command like this: %s ServerIPAddress\n",argv[0]);
exit(1);
}
int i=0;
for(i;i<1000;i++) //做循环是为了不想结束程序,想多次传输
SendRequest(argv); //传输的核心当然是从这里开始了
return 0;
}
通过上面我们知道了问题的核心就集中在SendRequest(argv)上,我们继续往下走:
void SendRequest(char**argv)
{
int client_socket = socket(AF_INET,SOCK_STREAM,0); //创建客户端连接套接字
int ret; //建立连接的返回值
if(client_socket<0)
{
printf("Create socket failed!\n");
exit(1);
}
struct sockaddr_in server_addr; //设置一个socket地址结构server_addr,代表服务器的internet地址, 端口
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
if(inet_aton(argv[1],&server_addr.sin_addr)==0) //服务器的IP地址来自程序的参数
{//inet_aton将一个参数的点分十进制IP地址转换为32位网络字节序二进制IP地址
printf("Server IP address error!\n");
exit(1);
}
server_addr.sin_port = htons(SERVER_PORT);
socklen_t server_addr_length = sizeof(server_addr);
//向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接
if((ret=connect(client_socket,(struct sockaddr*)&server_addr,server_addr_length))<0)
{
printf("Connect %s failed!\n",argv[1]);
exit(1);
}
上边这段应该还行吧,先创建一个客户端套接口,这个很重要,后面的和服务器连接,接收服务器信息都用这个套接口。然后就是定义服务器端套接口(server_addr),里边包含了IP地址啦,端口信息啦,反正靠这个就能找到远端服务器,接下来,就是将我们刚创建的本地套接口client_socket和服务器套接口建立一个通信管道(连接),这里很明显就是connect,是不。继续:
char file_name[FILE_NAME_MAX_SIZE+1]; //存储文件名的缓冲区
bzero(file_name,FILE_NAME_MAX_SIZE+1);
printf("Please enter the file name you want to open:\t");
scanf("%s",file_name); //输入要获得的文件的名(对应服务器端的完整路径)
char buffer[BUFFER_SIZE];
bzero(buffer,BUFFER_SIZE);
strncpy(buffer,file_name,strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name));
send(client_socket,buffer,BUFFER_SIZE,0); //向服务器发送buffer中的数据,就是告诉服务器我要buffer里对应的文件
FILE * fp = fopen(file_name,"w"); //同时在本地建立一个这样的文件,以便后面服务器传来的文件内容放进去
这一段也应该没有问题,注释中已经说的很明白了。一旦在上面建立了连接,这时就要告诉服务器我要接受你的啥文件(file_name),然后本地创建一个文件以便准备接收服务器传过来的东西。
bzero(buffer,BUFFER_SIZE); //刚buffer里存的是客户端想要的文件名,现在我们里边打算放服务器传过来的文件内容
int length = 0;
int write_length=0; //每次从服务器端接收文件内容的大小
while(length=recv(client_socket,buffer,BUFFER_SIZE,0)) //文件可能很大,一次只能接收一点,所以当然要用循环接收完
{//client_socket派上用场了,我前边说过,它和服务器的套接口地址信息建立了一个管道,那么服务器来的信息当然就存在它里边了,我们只要拷贝到buffer就好了
if(length<0) //奇怪,我居然没从里边接收到内容即内容长度为空
{
printf("Receive data from %s failed!\n",argv[1]);
break;
}
write_length = fwrite(buffer,sizeof(char),length,fp); //成功的服务器写入客户端套接字的client_socket中读到了数据,那就写到fp中吧
if(write_length<length) //写入的怎么会小于接收到的呢, 肯定出错了
{
printf("File %s write failed!\n",file_name);
break;
}
bzero(buffer,BUFFER_SIZE); //为了避免混淆,还是清空接收的缓冲区吧
}
if(write_length<=0) //这明显是出错了啊
{
printf("File %s dosen't exist!\n",file_name);
exit(1);
}
这一段是关键,我刚才说了,既然在本地套接口client_socket和服务器信息套接口地址上建立了连接,那么和服务器的所有通信就丢给了client_socket.一旦服务器有信息要写回给客户端,客户端的接口就是client_socket,r,然后我们只要把这里边的信息写入buffer,然后把buffer里的信息写入本地文件的文件描述符就好了(fd).最后说明的是我们用了循环,是因为谁也不能拍着胸脯说,能一下子接收完,网络的能力还是有限的,那就循环判断当服务器发过来的信息长度为0时,我就认为是传输结束了。当所有的一切都结束了,好那就关闭以前建立的连接啦,文件描述符啦,套接口啦:
close(fp);
close(client_socket);
}
代码讲完了,其实相当简单,刚都说了,我是多此一举来总结下大家都知道,最后给你一个流程图:
有了上面的流程图,现在清晰多了,服务器端的部分,我下次再讲。