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


需求

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

思路

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

代码

server端

/*************************************************************************
  > File Name: server.c
  > Author: KrisChou
  > Mail:zhoujx0219@163.com 
  > 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:zhoujx0219@163.com 
    > 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;
}

LINUX网络编程TCP服务器 客户端 有乱码怎解决?

解决办法:
1.在客户端n=read(socketfd,buff,1023);代码之前加上memset(buff,0,sizeof(buff));,这是保证收到较短数据(使用TCP你不能保证每次接收的数据和发送的数据时等长的),打印也是正确的;
2.将客户端buff[n+1]+='\0';修改为buff[n]='\0';,这是因为n是下标,已经是最后一个位置了;
3.将服务器端buff[n+1]+='\0';修改为buff[n]='\0';,这是因为n是下标,已经是最后一个位置了,而且和第2)一样,那个加号也要去掉,应该是笔误吧;
4.最大的问题,将服务器端write(connectfd,buff,1023);,你怎么能够保证收到1023个字符呢?也应该将while中条件移出作为WHILE中的一条语句,而且加上前面所述的memset语句,而将这里的write(connectfd,buff,1023);修改为write(connectfd,buff,strlen(buff))。
祝共同进步!
 

linux socket网络编程代码

Linux是多任务的操作系统,可在运行在Intel 80386及更高档次的PC机、ARMS、MIPS和PowerPC等多种计算机平台,已成为应用广泛、可靠性高、功能强大的计算机操作系统,Linux具有内核小、效率高、源代码开放等优点,还内含了TCP/IP网络协议,很适合在服务器领域使用,而服务器主要用途之一就是进行网络通信,随着计算机办公自动化处理技术的应用与推广,网络的不断普及,传统的纸张式文件传输方式已经不再适合发展的需要,人们更期待一种便捷、高效、环保、安全的网络传输方式.

协议概述TCP/IP即传输控制协议/网络协议[1](Transmission Control Protocol/Internet Protocol),是一个由多种协议组成的协议族,他定义了计算机通过网络互相通信及协议族各层次之间通信的规范,图1描述了Linux对IP协议族的实现机制[2]。

Linux支持BSD的套接字和全部的TCP/IP协议,是通过网络协议将其视为一组相连的软件层来实现的,BSD套接字(BSD Socket)由通用的套接字管理软件支持,该软件是INET套接字层,用来管理基于IP的TCP与UDP端口到端口的互联问题,从协议分层来看,IP是网络层协议,TCP是一个可靠的端口到端口的传输层协议,他是利用IP层进行传接报文的,同时也是面向连接的,通过建立一条虚拟电路在不同的网路间传输报文,保证所传输报文的无丢失性和无重复性。用户数据报文协议(User Datagram Protocol,UDP)也是利用IP层传输报文,但他是一个非面向连接的传输层协议,利用IP层传输报文时,当目的方网际协议层收到IP报文后,必须识别出该报文所使用的上层协议(即传输层协议),因此,在IP报头上中,设有一个"协议"域(Protocol)。通过该域的值,即可判明其上层协议类型,传输层与网络层在功能说的最大区别是前者提供进程通信能力,而后者则不能,在进程通信的意义上,网络通信的最终地址不仅仅是主机地址,还包括可以描述进程的某种标识符,为此,TCP/UDP提出了协议端口(Protocol Port)的概念,用于标识通信的进程,例如,Web服务器进程通常使用端口80,在/etc/services文件中有这些注册了的端口地址。

对于TCP传输,传输节点间先要建立连接,然后通过该连接传输已排好序的报文,以保证传输的正确性,IP层中的代码用于实现网际协议,这些代码将IP头增加到传输数据中,同时也把收到的IP报文正确的传送到TCP层或UDP层。TCP是一个面向连接协议,而UDP则是一个非面向连接协议,当一个UDP报文发送出去后,Linux并不知道也不去关心他是否成功地到达了目的的主机,IP层之下,是支持所有Linux网络应用的网络设备层,例如点到点协议(Point to Point Protocol,PPP)和以太网层。网络设备并非总代表物理设备,其中有一些(例如回送设备)则是纯粹的软件设备,网络设备与标准的Linux设备不同,他们不是通过Mknod命令创建的,必须是底层软件找到并进行了初始化之后,这些设备才被创建并可用。因此只有当启动了正确设置的以太网设备驱动程序的内核后,才会有/dev/eth0文件,ARP协议位于IP层和支持地址解析的协议层之间。

网络通信原理所有的网络通信就其实现技术可以分为两种,线路交换和包交换,计算机网络一般采用包交换,TCP使用了包交换通信技术,......余下全文>>
 

相关内容