zoukankan      html  css  js  c++  java
  • unix网络编程 服务器和客户传递程序

    一、一个简单的服务器和客户对接之后发送“hello world”的程序。

    服务器程序:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<sys/socket.h>
    #include<sys/types.h>
    #include<errno.h>
    #include<netinet/in.h>
    #include<sys/wait.h>
    
    #define LISTENQ 5000
    
    int main(int argc,char *argv[])
    {
        int sockfd,servfd,cliefd;
        struct sockaddr_in servaddr;
        struct sockaddr_in cliaddr;
        
        if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
        {
           printf("socket error");
           return(-1);
        }
        
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family=AF_INET;
        servaddr.sin_port=htons(8000);
        servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
        
        if(bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
        {   
            printf("bind error");
            exit(0);
        }
        
        listen(sockfd,LISTENQ);
        
        while(1)
        {
            printf("waitting fo connect!!
    ");
           if((cliefd=accept(sockfd,(struct sockaddr*)NULL,NULL))!=-1)
           {
              printf("连接成功!!
    ");
              send(cliefd,"hello world!",12,0);
           }
           else
           {
               printf("连接失败!!
    ");
            }
         close(cliefd);
         }
         close(sockfd);
         return 0;
    }

    客户程序:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<sys/socket.h>
    #include<sys/types.h>
    #include<errno.h>
    #include<netinet/in.h>
    #include<sys/wait.h>
    #include"apue.h"
    
    #define MAX 1500
    
    int main(int argc,char *argv[])
    {
         int sockfd,cliefd;
         struct sockaddr_in servaddr;
         char  buf[MAXLINE];
         
         if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
         {
             printf("socket error");  
             return(-1);
         }
         
         bzero(&servaddr,sizeof(servaddr));
         servaddr.sin_family=AF_INET;
         servaddr.sin_port=htons(5000);
         if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<=0)//关键部分
         err_quit("inet_pton error for%s,argv[1]");
    
         
         
         if(connect(sockfd,(struct sockaddr*)&cliaddr,sizeof(struct sockaddr))==-1)
         {
           printf("连接失败!!!
    ");
         }
         else
         {
            printf("连接成功!!!
    ");
            recv(sockfd,buf,MAX,0);
            printf("%s",buf);
         }
        close(sockfd);
        return 0;
    }

    编译过程中出现的问题以及原因:

    1.服务器的 printf ("连接成功!!")无法输出

    原因:在linux系统下,printf函数是行缓冲式的输出,当printf遇到 时,或者缓冲区满时,才会将缓冲区里的内容刷新到标准输出(stdout).因此, printf("连接成功!!"); 等语句的显示不能立刻显示在屏幕上,但是printf("连接成功!! "); 可以

    2.客户中的struct sockaddr_in对应的地址是哪个?

    是服务器的地址描述符(servaddr)

    3.bzero后面的参数写什么?

    都是对于servaddr的处理

    4.最大监听数要自己定义?

    对于undp上的代码,最大监听数包含在头文件#include “und.p”,但是在我们自己写的代码要交代清楚

    5. if((cliefd=accept(sockfd,(struct sockaddr*)NULL,NULL))!=-1)

    accept后面的参数是指客户的一些消息,如果不要就直接写NULL。

    6.关于send()和recv()

    send(SOCKET s,const char FAR*buf,int len,int flags)

    我的理解:把buf中的内容以len的长度(以flags的形式)复制到s的内容中(发送到协议中)

    s:一个用于标识已连接套接字的描述字

    buf:包含待发送数据的缓冲区

    len:缓冲区数据的长度

    flags:调用执行方式

    send()是一个计算机函数,功能是向一个已经连接的socket发送数据,如果无错误,返回值为所发送数据的总数,否则返回SOCKET_ERROR

    recv(SOCKET s,char *buf,int len,int flags)

    我的理解:把s的内容以len长度(以flags的形式)复制到数组buf中(从协议中接收)

    s:一个用于标识已连接套接字的描述字

    buf:存放从缓冲区读取的内容

    len:缓冲区的长度

    flags:指定调用的方法

    recv()先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR;

     注意:recv函数仅仅是copy数据,真正的接收数据是协议来完成的。recv函数返回其实际copy的字节数

    7.服务器中要写上close(sockfd);语句

    因为写上close,在调用完之后就会关闭相关联系

    8.inet_pton

    这是一个ip地址转换函数,在点分十进制和二进制整数之间的转换,可以处理IPv4和IPv6

    点分十进制是IPv4地址标识方法。IPv4中用四个字节表示一个ip地址,每个字节按照十进制表示0~255。点分十进制就是用4个从0~255的数字,来表示一个ip地址。

    函数原型:

    int inet_pton(int af,const char *src,void *dst);

    这个函数转换字符串到网络地址,第一个参数af是地址族,第二个参数*src是来源地址,第三个参数*dat接收转换后的数据

    9.程序中可能会运行不了,是因为会遇到不同的端口号,把端口号改成合适的就可以运行了 

    二、一个从服务器获得要的图片/文件的程序

    服务器程序:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<sys/socket.h>
    #include<sys/types.h>
    #include<errno.h>
    #include<netinet/in.h>
    #include<sys/wait.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<sys/stat.h>
    
    #define BUFFER_SIZE 1024
    #define FILE_NAME_SIZE 500
    #define LISTENQ 5000
    
    int main(int argc,char *argv[])
    {
        int sockfd,servfd,cliefd;
        struct sockaddr_in servaddr;
        struct sockaddr_in cliaddr;
        
        if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
        {
           printf("socket error");
           return(-1);
        }
        
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family=AF_INET;
        servaddr.sin_port=htons(8080);
        servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
        
        if(bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
        {   
            printf("bind error");
            exit(0);
        }
        
        listen(sockfd,LISTENQ);
        
        
        while(1)
        {
           printf("waitting fo connect!!
    ");
          if((cliefd=accept(sockfd,(struct sockaddr*)NULL,NULL))==-1)
           {   printf("连接失败
    ");     }else
           {   printf("连接成功
    ");     }
            
            //记录文件内容
            char buffer[BUFFER_SIZE];
            bzero(buffer,BUFFER_SIZE);
            
            //把cliefd对应的客户的具体内容接收到buffer中,等待协议接收数据中断,返回-1
            //实际上,recv只是把协议传输过来的数据进行复制而已
            int length=recv(cliefd,buffer,BUFFER_SIZE,0);
            if(length<0)
            {
               printf("文件获取失败
    ");
               break;
            }
            
            //记录文件的名字
            char file_name[FILE_NAME_SIZE+1];
            bzero(file_name,FILE_NAME_SIZE+1);
            
            //strncpy是c语言一个复制字符串的函数
            //函数原型strncpy(char *dest, const char *src, int n),把src的前n个字符复制到dest中
            strncpy(file_name,buffer,strlen(buffer)>FILE_NAME_SIZE?FILE_NAME_SIZE:strlen(buffer));
            
             //文件指针记录文件的打开的情况(rb是指以二进制的形式打开文件。这样就可以打开文档和图片等类型的文件)
             FILE *fpp=fopen(file_name,"rb");
             if(NULL==fpp)
             {
                  printf("File %s Not Found
    ",file_name);
             }
             else
             {
                 bzero(buffer,BUFFER_SIZE);
                 int file_block_length=0while((file_block_length=fread(buffer,sizeof(char),BUFFER_SIZE,fpp))>0)
             {
                   printf("file_block_length=%d
    ",file_block_length);
                   if(send(cliefd,buffer,file_block_length,0)<0)
                 {
                    printf("Send File %s faild",file_name);
                    break;
                 }
                 bzero(buffer,BUFFER_SIZE);
               }
               //close(fp);
               fclose(fpp);
               printf("File %s transfer finished
    ",file_name);
              }
          //关闭与客户端的连接
          close(cliefd);
         }
         //关闭监听
        close(sockfd);
         return 0;
    }

    客户程序:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<sys/socket.h>
    #include<sys/types.h>
    #include<errno.h>
    #include<netinet/in.h>
    #include<sys/wait.h>
    #include"apue.h"
    
    #define MAX 1500
    #define FILE_NAME_SIZE 512
    #define BUFFER_SIZE 1024
    
    int main(int argc,char *argv[])
    {
         int sockfd,cliefd;
         struct sockaddr_in servaddr;
         char  buf[MAXLINE];
         
         if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
         {
             printf("socket error");  
             return(-1);
         }
         
         bzero(&servaddr,sizeof(servaddr));
         servaddr.sin_family=AF_INET;
         servaddr.sin_port=htons(8080);
         if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<=0)//关键部分
         err_quit("inet_pton error for%s,argv[1]");
    
         
         
         if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr))==-1)
         {printf("连接失败!!!
    ");}else
         {printf("连接成功!!!
    ");}
         
         char file_name[FILE_NAME_SIZE+1];
         bzero(file_name,FILE_NAME_SIZE+1);
         printf("Please input the name of the file:");
         scanf("%s",file_name);
    
         char buffer[BUFFER_SIZE];
         bzero(buffer,BUFFER_SIZE);
         strncpy(buffer,file_name,strlen(file_name)>FILE_NAME_SIZE?FILE_NAME_SIZE:strlen(file_name));
         
         send(sockfd,buffer,BUFFER_SIZE,0);
       
         FILE *fp=fopen(file_name,"wb");
         if(NULL==fp)
         {
           printf("file %s can not to write
    ",file_name);
           exit(1);
          }
         
          //从服务器接收数据到buffer中
          bzero(buffer,BUFFER_SIZE);
          int length=0;
          while(length=recv(sockfd,buffer,BUFFER_SIZE,0))
          {
             if(length<0)
            {
               printf("reveive data from server %s fail
    ",file_name);
               break;
             }
            int write_length=fwrite(buffer,sizeof(char),length,fp);
            if(write_length<length)
            {
            printf("file %s write faild
    ",file_name);
            break;
            }
            bzero(buffer,BUFFER_SIZE);
           }
           printf("receive file %s from server finish
    ",file_name);
    
          fclose(fp);
          close(sockfd);
        return 0;
    }

    编译过程中出现的问题以及原因:

    1.完成该过程的思路是什么?

    当连接阶段完成之后,我们就可以互相传输文件。文件包括文件名和文件内容,这两个变量我们都要交代清楚。文件传输大致是这样的:现在两边都声明文件名和存放文件的缓存区名,然后把相应的存储空间清零。我们在客户端写入我们想要的文件名,名字放在缓存区中,发送给服务器。服务器从缓冲区中接收到信息,通过recv复制到服务器中,服务器通过fopen以二进制的形式查找到相应的内容,把内容通过send放在缓冲区中。客户端从缓冲区中读取到内容,通过recv复制到我们声明好的文件变量对应的文件,然后用fwrite打开并写入其中。

    特别有一点要注意的,recv()和send()这两个函数可以理解为连接服务器、客户端以及缓冲区,它们的功能和copy差不多,实际上数据的复制是在协议中进行的。

    2.函数strncpy(char *dest, const char *src, int n)

    strncpy是c语言复制字符的函数。把src的前n个字符复制到dest中

    3.fopen()、fwrite()、fread()

    函数原型:fopen(const char * path,const char * mode)

    进一步理解:fopen(文件名,使用文件方式)

    fopen是用来实现文件打开的函数

    使用文件的方式

    r 只读
    w 只写
    rb 为了输入数据,打开一个二进制文件
    wb 为了输出数据,打开一个二进制文件
       

    用二进制的方式向文件读一组数据

    fread(buffer,size,count,fp);

    从文件中读一个数据块

    buffer:用来存放从文件读入的数据的存储区的地址

    size:要读的字节的大小

    count:要读多少个数据项(每个数据项长度为size)

    fp:FILE类型指针

    用二进制的方式向文件写一组数据

    fwrite(buffer,size,sount,fp);

    向文件写一个数据块

    buffer:把此地址开始的存储区中的数据向文件输出

    size:要写的字节的大小

    count:要写多少个数据项(每个数据项长度为size)

    fp:FILE类型指针

     4.ping

    ping是window、linux中一个命令。

    ping也属于一个通信协议,是TCP/IP协议的一部分

    利用ping命令可以检查网络是否连通,可以很好地帮助我们分析和判定网络故障

    应用格式:Ping空格 IP地址

    三、一个服务器和客户端通信的程序

    服务器程序:

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<string.h>
    #include<netinet/in.h>
    #include<errno.h>
    #include<sys/wait.h>
    
    #define MAX 5000
    #define LISTENQ 1024
    
    int main(int argc,char *argv[])
    {
         int sockfd,cliefd;
         struct sockaddr_in servaddr,cliaddr;
         char cli[MAX],ser[MAX];
         
         if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
         {
             printf("socket error
    ");
             exit(0);
         }
        
         bzero(&servaddr,sizeof(servaddr));
         servaddr.sin_family=AF_INET;
         servaddr.sin_port=htons(8080);
         servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
         
        int opt = 1;
        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) != 0)
        {           
        perror("Server setsockopt failed");
        return 1;
        }
         if((bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)))<0)
         {
               printf("bind error:%s
    ",strerror(errno));
               exit(0);
         }
         
         listen(sockfd,LISTENQ);
    
        while(1)
       { 
         printf("waiting for connecting.
    ");
         if((cliefd=accept(sockfd,(struct sockaddr*)NULL,NULL))<0)
        {
            printf("connect faild
    ");
            exit(1);
        }
        else
        {
            printf("connect success
    ");
        }
        printf("welcome to chatting rome!!
    ");
        
        while(1)
        {
          
          recv(cliefd,cli,500,0);
          if(cli[0]=='#') 
          {
            printf("From client: %s
    ",cli);
            printf("Over
    ");
            exit(0);
           }
          else
          {
            printf("From client: %s
    ",cli);
           // bzero(&cli,sizeof(cli));
            
            printf("To client:");
            scanf("%s",ser);
            send(cliefd,ser,500,0);
            if(ser[0]=='#')
            {
              printf("Over
    ");
          bzero(ser,0);
              bzero(&cliefd,sizeof(cliefd));
              return(-1);
            }
           }
         }
        close(cliefd);
        }
        close(sockfd);
       return 0;
    }            

    客户端程序:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<sys/socket.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #include<netinet/in.h>
    #include<errno.h>
    
    #define MAX 1500
    
    int main(int argc,char *argv[])
    {
        int sockfd;
        struct sockaddr_in servaddr;
        char ser[MAX],cli[MAX];
    
        if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
        {
          printf("soket error
    ");
          exit(0);
        }
       
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family=AF_INET;
        servaddr.sin_port=htons(8080);
        inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
        
        if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr))<0)
        {
          printf("connect error:%s
    ",errno);
          exit(0);
        }
        else
        {
           printf("connect success
    ");
           printf("welcome to chatting rome!
    ");
        }
        
        while(1)
       {
         printf("To server:");
         scanf("%s",cli);
         send(sockfd,cli,500,0);
         if(cli[0]=='#')
         {
            printf("Over
    ");
            exit(0);
         }
         else
         {  
            bzero(&cli,sizeof(cli));
            recv(sockfd,ser,500,0);
            printf("From server: %s
    ",ser);
            if(ser[0]=='#')
            {
               printf("Over
    ");
               exit(0);
            }
            bzero(&ser,sizeof(ser));
          }
        }
        close(sockfd);
        return 0;
    }

    编译过程中出现的问题以及原因:

    1.server完成一次对接之后(在server结束),再次执行程序,出现address already in use这样的错误信息,表示我端口已经被占用

    (参考资料https://www.cnblogs.com/CodeMIRACLE/p/5122063.html

    只要在bind函数之前加入下面代码即可:

    int opt = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) != 0)
    {           
        perror("Server setsockopt failed");
        return 1;
    }

     bind普遍遭遇的问题是试图绑定一个已经在使用的端口,该陷阱是也许没有活动的套接字存在,但仍然禁止绑定端口,它是由TCP套接字状态TIME_WAIT引起的。在TIME_WAIT状态退出之后,套接字被删除,该地址才能被重新绑定而不出问题。

    2.怎么查看程序运行失败是出现什么问题?

    因为我们在头文件里有交代#include<errno.h>,所以我们是可以通过调用strerror(errno)来查看到底是什么错误

    strerror

    头文件:#include<string.h>

    作用:通过标准错误的标号,获得错误的描述字符串 ,将单纯的错误标号转为字符串描述,方便用户查找错误

    参数:错误标号(errno)

    errno

    是记录系统的最后一次错误代码,代码是一个int型的值,每一个值表示着不同的含义,可以通过查看该值推测出错的原因

    3.通配符地址(INADDR_ANY)

    INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或"所有地址"、"任意地址"。 一般来说,在各个系统中均定义成为0值

  • 相关阅读:
    动态横向(水平)合并Repeater数据行DataItem的列
    动态绑数据(Repeater控件HeaderTemplate和ItemTemplate)
    动态横向(水平)合并GridView数据行DataRow的列
    动态绑数据(GridView控件Header和ItemTemplate)
    用具体列名替代星号
    如何实现数据行转换列显示
    用LINQ获取XML节点数据
    从字符串中获取XML节点数据
    字符串创建XML文档
    根据Attribute值条件对XML文档进行修改
  • 原文地址:https://www.cnblogs.com/181118ljh123/p/12002316.html
Copyright © 2011-2022 走看看