基于linux的文件传输器实现详解----服务器端实现详解

来源:互联网 发布:西南证券炒股软件 编辑:程序博客网 时间:2024/06/17 05:56

继续前边上一节说过的内容,通过上节知道了,客户端的实现细节,现在来分析服务器端的实现细节。

       上节的岔路口在于客户端告诉了服务器自己想要的文件以及服务器把文件内容写回给套接字通信信道那里,那我的内容就从这里继续开始。

       首先,还是要从C语言的main()开始:

view sourceprint?
01int main(int argc,char**argv)
02{
03    //设置一个socket地址结构server_addr,代表服务器internet地址, 端口
04    structsockaddr_in server_addr;
05    //把一段内存区的内容全部设置为0
06    bzero(&server_addr,sizeof(server_addr));
07    server_addr.sin_family = AF_INET;
08    server_addr.sin_addr.s_addr = htons(INADDR_ANY);
09    server_addr.sin_port = htons(SERVER_PORT);
10      
11    //创建用于internet的流协议(TCP)socket,用server_socket代表服务器socket
12    intserver_socket = socket(AF_INET,SOCK_STREAM,0);
13    if(server_socket<0)
14    {
15        printf("Create socket failed!\n");
16        exit(1);
17    }

       在客户端的学习中,有了经验,这里就很简单了,服务器端先创建一个基于自己的套接字以及定义本地(不同于客户端的本地,这里的本地指的是服务器)的套接口地址信息结构,这两个个后面会用到:

view sourceprint?
1if(bind(server_socket,(structsockaddr*)&server_addr,sizeof(server_addr)))
2{//把socket和socket地址结构联系起来
3    printf("Server bind port: %d failed!\n",SERVER_PORT);
4    exit(1);
5}

       然后把上面创建的本地套接字和本地套接口地址信息结构绑定到一起,以便后面的通信能找到:

view sourceprint?
1if(listen(server_socket,LENGTH_OF_LISTEN_QUEUE))
2{   //server_socket用于监听
3    printf("Server listen failed!\n");
4    exit(1);
5}

       这个listen看起来挺明显吧,其实作用是这样的:我们前边使用socket创建的server_socket,它是被假设为一个主动套接口,也就是说是一个将调用connect发起连接的客户套接口,但我们这里是服务器端,是被动接受连接的啊,咋办?唉,listen就把这样一个未连接的套接口由主动变为了被动,指示内核应接受指向该套接口的连接请求,这个函数的第二个参数给出了内核应该为相应的套接口排队的最大连接数。

view sourceprint?
01while(1)//服务器端当然要一直运行
02{
03    structsockaddr_in client_addr;    //定义客户端的socket地址结构client_addr
04    socklen_t length =sizeof(client_addr);     
05    //接受一个到server_socket代表的socket的一个连接
06    intnew_server_socket = accept (server_socket,(structsockaddr*)&client_addr,&length);
07    if(new_server_socket<0)  //居然失败了,唉
08    {
09        printf("Server accept failed!\n");
10        break;
11    }

       这里的关键就是accept函数了,它用于从已完成连接队列队头返回下一个已完成连接。如果没有连接,那它就休眠吧。后面两个参数用来保存取得的对端进程(即客户端)的协议地址信息。new_server_socket是由内核自动生成的一个全新的与所返回可以的TCP连接。

view sourceprint?
01int child_process_pid = fork(); //fork()后,子进程是主进程的拷贝
02if(child_process_pid == 0 )//如果当前进程是子进程,就执行与客户端的交互
03             {
04    close(server_socket);//子进程中不需要被复制过来的server_socket
05    charbuffer[BUFFER_SIZE];
06    bzero(buffer,BUFFER_SIZE);
07    length = recv(new_server_socket,buffer,BUFFER_SIZE,0);
08    if(length<0) //居然出错了,唉
09    {
10        printf("Server receive data failed!\n");
11        break;
12    }

      这时从客户端发过来的信息就通过上面说的TCP连接的通信信道new_server_socket传过来,我们这里就用recv读入到buffer里边。

view sourceprint?
1char file_name[FILE_NAME_MAX_SIZE+1];
2bzero(file_name,FILE_NAME_MAX_SIZE+1);
3strncpy(file_name,buffer,strlen(buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer));
4FILE * fp = fopen(file_name,"r");  //打开客户端要求的文件,名字是刚才已经拷到buffer又拷打file_name里了
5if(fp==NULL)   //没有这个文件,客户端搞错了.
6{
7    printf("File:\t%s not found\n",file_name);
8}

       注释里说的明白,我们已经把客户端需要的文件名从通信管道new_server_socket拷到了buffer,这时只要利用file_name这个桥梁(等同buffer)打开服务器端的这个文件就好了。

view sourceprint?
1else
2{
3    bzero(buffer,BUFFER_SIZE);//buffer里存放的是客户端要的文件名,我打算后面放文件内容,所以还是清空吧
4    intfile_block_length = 0;
5    //发送buffer中的字符串到new_server_socket,实际是给客户端
6    while((file_block_length =fread(buffer,sizeof(char),BUFFER_SIZE,fp))>0)

       看到这里的fread了没?它就把刚开服务器按照用户要求的文件打开,然后从里边读了BUFFER_SIZE的东西在buffer里了。

view sourceprint?
01    {
02        printf("file_block_length = %d\n",file_block_length);
03        if(send(new_server_socket,buffer,file_block_length,0)<0)
04        {//向客户端写文件信息
05            printf("Send file:\t%s failed!",file_name);
06            break;
07        }
08        bzero(buffer,BUFFER_SIZE);//没写一次当然要清空一下
09    }
10    fclose(fp);//最后都做完了,好习惯,关闭相关的文件描述符
11    printf("File:\t%s transfer finished\n",file_name);
12}

       这里就不多说了,关键的send,我们的最终目的就是要把读到的文件信息从buffer中发回到通信管道,那端对着客户端呢,好sent正和我意。还有就是我这里用了循环,是因为网络总是能力有限,所以我每次从文件里读内容时是根据一定的大小定量读的。

view sourceprint?
01            close(new_server_socket); 
02                    exit(0);         
03        }
04        elseif(child_process_pid > 0)    //如果当前进程是主进程 
05                    close(new_server_socket);   //主进程中不需要用于同客户端交互的new_server_socket
06    }
07      
08    //关闭监听用的socket
09    close(server_socket);
10    return0;
11}

      最后的清理工作我就不说了,善始善终嘛,是不?

       代码讲完了,其实相当简单,刚都说了,我是多此一举来总结下大家都知道,最后给你一个流程图:

           QQ截图20110721165003

       有了上面的流程图,现在服务器端的也清晰多了。你开心,我的任务也彻底完成了。

原创粉丝点击