Linux网络编程6——使用TCP实现文件服务器

需求

当客户端连接上服务器后,服务器会将相应文件传输给客户端,实现文件下载。

思路

服务器端,主进程负责listen。循环内,主进程每从任务请求队列中accept出一个请求,就fork出孙子完成文件传输。注意:如果只是fork出儿子,那么主进程就得wait儿子,这样的话,只有当给一个客户端传完文件后才能下一个。

代码

server端

/*************************************************************************
  > File Name: server.c
  > Author: KrisChou
  > Mail:[email protected]
  > Created Time: Thu 28 Aug 2014 09:40:32 PM CST
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#define SNDSIZE 1024*1024 //1M,注意如果栈空间(可以设置的)没这么大,就用堆空间
#define FILE_NAME  "a.rmvb"

int main(int argc,char* argv[])// exe config
{
    /* 从配置文件中读取服务器联系方式:IP以及端口号 */
    int fd_conf ;
    fd_conf = open(argv[1], O_RDONLY) ;
    FILE* fp_conf = fdopen(fd_conf, "r"); //秀一下fdopen函数,文件描述符转换为文件指针 fdopen(int fd, const char *mode)
    char my_ip[32]="";
    int my_port ;
    fscanf(fp_conf, "%s%d", my_ip, &my_port);
    close(fd_conf);
    fclose(fp_conf);

    /* 创建服务器的监听socket端口 */
    int fd_listen
    fd_listen = socket(AF_INET, SOCK_STREAM, 0);
    if(fd_listen == -1)
    {
        perror("socket");
    }

    /* 为服务器socket端口绑定联系方式,以便让客户端connect */
    struct sockaddr_in  server_addr ;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET ;
    server_addr.sin_port = htons(my_port);
    server_addr.sin_addr.s_addr  = inet_addr(my_ip);
    if(-1 == bind(fd_listen, (struct sockaddr *)&server_addr, sizeof(server_addr)) )
    {
        perror("bind");
        close(fd_listen);
        exit(1);
    }
    /* listen */
    if(-1 == listen(fd_listen, 5))
    {
        perror("listen");
        close(fd_listen);
        exit(1);
    }

    /* accept */
    int fd_client;  /* 客户端socket端口的另一头 */
    struct sockaddr_in client_addr ; /* 客户端联系方式,以accept函数的传出参数给出 */
    int len ;
    memset(&client_addr, 0, sizeof(client_addr));
    len = sizeof(client_addr);
    while(1)
    {
        fd_client = accept(fd_listen, (struct sockaddr *)&client_addr, &len);
        if(fd_client == -1)
        {
            continue ;
        }
        printf("a client connect ! ip:port  %s:%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
        if(fork() == 0)
        {
            close(fd_listen); //对于子进程和孙子进程都不需要listen端口,直接关闭。
            if(fork() == 0)
            {

                int fd_file = open(FILE_NAME, O_RDONLY); //打开客户端要下载的文件
                char buf[SNDSIZE] ;
                int snd_cnt = 0 ;
                int readn ;
                /* 从文件中读取数据,并发送给客户端 */
                while(memset(buf, 0,SNDSIZE ), (readn = read(fd_file,buf, SNDSIZE)) > 0)
                {
                        if(send(fd_client, buf, readn, 0) != readn)
                        {
                            printf("send error! \n");
                        }
                        snd_cnt ++ ;
                }
                printf("send over: %d \n", snd_cnt);
                close(fd_file);
                close(fd_client);
                exit(0);
            }
            close(fd_client);
            exit(0);
        }
        close(fd_client);
        wait(NULL) ;

    }

    return 0 ;
}

client端

/*************************************************************************
    > File Name: client.c
    > Author: KrisChou
    > Mail:[email protected]
    > Created Time: Thu 28 Aug 2014 11:59:20 PM CST
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#define SNDSIZE 1024*1024
#define SERVER_PORT 6080 // 服务器端口

int main(int argc, char *argv[]) EXE IP
{
    /* socket */
    int fd_client;
    fd_client = socket(AF_INET, SOCK_STREAM, 0);
    if(fd_client == -1)
    {
        perror("socket");
        exit(1);
    }
    /* 存放所连服务器信息的结构体 */
    struct sockaddr_in server_addr;
    memset(&server_addr,0,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    /* connect */
    while(connect(fd_client, (struct sockaddr *)&server_addr,sizeof(server_addr)) == -1)
    {
        /* 当connect返回-1时,说明服务器还没有启动 */
        sleep(1);
        printf("connecting... \n");
    }
    printf("connect success! \n");

    int fd_write = open("txt", O_WRONLY | O_CREAT);//下载的内容写入本地文件中
    char buf[SNDSIZE];
    int readn;
    int recv_cnt = 0;
    while(memset(buf,0,SNDSIZE),(readn = recv(fd_client,buf,SNDSIZE,0)) > 0)
    {
        write(fd_write,buf,readn);
        recv_cnt++;
    }
    printf("recv over: %d \n", recv_cnt);
    close(fd_write);
    close(fd_client);
    return 0;
}
时间: 2024-08-03 09:39:23

Linux网络编程6——使用TCP实现文件服务器的相关文章

Linux网络编程--wireshark分析TCP包头的格式

摘要:     本文简介了TCP面向连接理论知识,具体讲述了TCP报文各个字段含义.并从Wireshark俘获分组中选取TCP连接建立相关报文段进行分析. 一.概述     TCP是面向连接的可靠传输协议,两个进程互发数据之前须要建立连接,这里的连接仅仅只是是端系统中分配的一些缓存和状态变量,中间的分组交换机不维护不论什么连接状态信息. 连接建立整个步骤例如以下(即三次握手协议): 首先,客户机发送一个特殊的TCP报文段: 其次,server用还有一个特殊的TCP报文段来响应: 最后,客户机再用

Linux网络编程——浅谈 TCP 三次握手和四次挥手

一.tcp协议格式 二.三次握手 在 TCP/IP 协议中.TCP 协议提供可靠的连接服务,採用三次握手建立一个连接. 第一次握手:建立连接时,client发送 syn 包(tcp协议中syn位置1.序号为J)到server,并进入 SYN_SEND 状态.等待server确认: 第二次握手:server收到 syn 包,必须确认客户的 SYN,同一时候自己也发送一个 SYN 包,即 SYN+ACK包(tcp协议中syn位置1,ack位置1.序号K,确定序号为J+1),此时server进入 SY

Linux网络编程二、tcp连接API

一.服务端 1.创建套接字: int socket(int domain, int type, int protocol); domain:指定协议族,通常选用AF_INET. type:指定socket类型,TCP通信下使用SOCK_STREAM. protocol:指定协议,通常为0. 返回值:成功则返回新socket的文件描述符,失败返回-1. 头文件:sys/socket.h     sys/types.h 2.绑定套接字 int bind(int sockfd, struct sock

【Linux 网络编程】常用TCP/IP网络编程函数

(1)函数socket 1 /**************************************************************** 2 ** 功能:创建一个套接字用于通信 3 ** 参数:domain 指定通信协议族 4 ** type 指定socket类型,流式套接字 SOCK_STREAM 5 ** 数据报套接字 SOCKDGRAM 6 ** 原始套接字 SOCKRAW 7 ** protocol 协议类型 (习惯上填写0) 8 ** 返回值:成功返回非负整数,它

Linux网络编程8&mdash;&mdash;对TCP与UDP的简易封装

引言 每次使用socket通信,都会有很对相似的操作.本文,会对TCP与UDP通信做一简单封装,并生成动态库. 代码 my_socket.h #ifndef __MY_SOCKET_H__ #define __MY_SOCKET_H__ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #in

Linux网络编程9&mdash;&mdash;对TCP与UDP的简易封装2.0

具体生成动态库的操作及使用该动态库的操作请参见上篇博文.以下仅仅列出改进版本的代码. 代码 my_socket.h #ifndef __MY_SOCKET_H__ #define __MY_SOCKET_H__ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys

Linux网络编程7&mdash;&mdash;使用TCP实现双方聊天

思路 主线程负责发送消息,另一线程负责接收消息.服务端和客户端均是如此. 注意 当A方close掉用于通信的socket端口后,该端口是不会立即关闭的.因为此时可能B方的信息还没send完.因此,此时A方的recv仍旧处于阻塞状态,会最后再等待收一次信息.此时,当B方send一个信息给A后,A方recv到后,A的socket端口就正式关闭了,A的recv返回-1. 此时由于A的socket端口已关闭,因此B得recv返回0. 代码 本代码服务器和客户端程序写在一起了.具体请看注释. /*****

Linux网络编程10&mdash;&mdash;使用UDP实现五子棋对战

思路 1. 通信 为了同步双方的棋盘,每当一方在棋盘上落子之后,都需要发送给对方一个msg消息,让对方知道落子位置.msg结构体如下: /* 用于发给对方的信息 */ typedef struct tag_msg { int msg_type; /* 悔棋? */ int msg_color; int msg_row; int msg_col; }MSG, *pMSG; 2. 悔棋 用链表头插法来模拟栈,链表记录了双方下子的轨迹.结构如下: /* 记录每一步的轨迹 */ typedef stru

linux网络编程笔记——TCP

1.TCP和UDP TCP是长连接像持续的打电话,UDP是短消息更像是发短信.TCP需要消耗相对较多的资源,但是传输质量有保障,UDP本身是不会考虑传输质量的问题. 2.网络传输内容 我习惯的做法是直接通过TCP传送结构体,当然前提是收发两端都在程序里对目标结构体有充分的定义.特别说明的一点是,要小心收发两端处理器的大小端问题!而且传输信息头里必须包含长度信息,而且通用的是大端.但是,这里的长度和结构体,我选择用小端进行传输. 3.TCPserver实现 参考了别人多线程的回调写法,看起来不错.