zoukankan      html  css  js  c++  java
  • 使用完整读写函数的网络应用程序

    使用完整读写函数的网络应用程序

    Linux的设计原则是将硬件设备抽象成文件,用户可以像操作文件一样操作设备,前边已经说过,我们可以使用文件操作函数操作套接字。

    使用read函数读取套接字另一端发送过来的内容,使用write函数也可以向套接字另一端发送内容。但是,在网络环境中有一个很大的问题就是延时的问题,对于本地文件夹来说,字节流在本地传输的延时可以忽略不计,但是网络环境中传输延时可能会很长造成I/O阻塞。其次,网络应用程序要能够处理因为中断或者网络连接等问题造成的读写操作异常返回。

    对于第一个问题,解决的办法只有使用非阻塞I/O或者多路I/O转接。对于第二个问题,网络环境的读写操作会常因为信号中断而异常返回,这时候read和write函数返回-1,errno错误号设置为EINTR,遇到这种情况应该从上一次成功读写的地方重新进行读写,对于其他出错一场,应该打出错误号和错误原因。因此可以设计一个新的read和write函数,屏蔽这种异常处理的细节。下面是my_read函数的执行流程图。

     

    my_read函数和my_write函数的代码如下

    my_read函数代码:

    ssize_t my_read(int fd, void *buffer, size_t length)
    {
           ssize_t done = length;                  ///读入的字节数
           while(done > 0)                         ///如果因为信号中断而导致异常则多次读取
           {
                done = read(fd, buffer, length);    ///调用read函数
                if(done == -1)
                if(errno == EINTR)              ///如果时信号中断导致的错误,则舍弃已读入的内容重新读
                         done = length;
                  else
                  {
                         perror("fail to read");
                         return -1;
                  }
           else
                  break;
           }
           return done;                            ///返回实际读入的字节数
    }            

    my_write函数代码:

    ssize_t my_write(int fd, void *buffer, size_t length)
    {
           ssize_t done = length;                      ///实际写入的字节数
           while(done > 0)                             ///如果因为信号中断而导致异常,则多次写缓冲区
           {
                  done = write(fd, buffer, length);       ///调用write函数
                  if(done != length)                      ///异常出错
                         if(errno == EINTR)                  ///如果时信号中断导致的错误,则舍弃已写入的内容,重新写缓存区
                                done = length;
                         else
                         {
                                perror("fail to write");
                                return -1;
                         }
                  else
                         break;
           }
           return done;                                ///返回实际写的字节数
    }

    可以将自己设计的my_read和my_write函数写到自己的文件I/O函数库,方便以后使用。添加相应的iolib.h文件,文件中包含对自己的文件库函数的声明,程序如下:

    extern ssize_t my_read(int fd, void *buffer, size_t length);
    extern ssize_t my_write(int fd, void *buffer, size_t length);

    在之前的客户端程序和服务器端程序中添加my_read函数和my_write函数就能进行出错和异常处理,代码如下:

    Server.c:

    #include <stdio.h>
    #include <unistd.h>
    #include <strings.h>
    #include <string.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include "iolib.h"
    #define MAX_LINE 100
    
    /*处理函数,用于将大写字符转换为小写字符。参数为需要转换的字符串*/
    void my_fun(char * p)
    {
           if(p == NULL)
                  return;
           for(;*p != ''; p++)
                  if(*p >= 'A' && *p <= 'Z')
                  *p = *p - 'A' + 'a';
    }
    
    int main(void)
    {
           struct sockaddr_in sin;
           struct sockaddr_in cin;
           int l_fd;
           int c_fd;
           socklen_t len;
           char buf[MAX_LINE];
           char addr_p[INET_ADDRSTRLEN];
           int port = 8000;
           int n;
           bzero(&sin, sizeof(sin));
           sin.sin_family = AF_INET;
           sin.sin_addr.s_addr = INADDR_ANY;
           sin.sin_port = htons(port);                  ///端口号转换为网络字节序
           if((l_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
           {
                  perror("fail to creat socket");
                  exit(1);
           }
           if(bind(l_fd, (struct sockaddr *) &sin, sizeof(sin)) == -1)
           {
                  perror("fail to bind");
                  exit(1);
           }
           if(listen(l_fd, 10) == -1) ///开始连续监听
           {
                  perror("fail to listen");
                  exit(1);
           }
           printf("waiting...
    ");
           while(1)
           {
                  if((c_fd = accept(l_fd, (struct sockaddr*) &cin, &len)) == -1)
                  {
                         perror("fail to accept");
                         exit(1);
                  }
                  n = my_read(c_fd, buf, MAX_LINE);
                  if(n == -1)
                         exit(1);
                  else if(n == 0)
                  {
                         printf("the connect has been closed
    ");
                         close(c_fd);
                         continue;
                  }
                  inet_ntop(AF_INET, &cin.sin_addr, addr_p, sizeof(addr_p));
                  printf("client IP is %s, port is %d
    ", addr_p, ntohs(cin.sin_port));
                  printf("content is : %s
    ", buf);
                  my_fun(buf);
                  n = my_write(c_fd, buf, n);
                  if(n == -1)
                  {
                         exit(1);
                  }
                  if(close(c_fd) == -1)
                  {
                         perror("fail to close");
                         exit(1);
                  }
           }
           return 0;
    }

    client.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <strings.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include "iolib.h"
    #define MAX_LINE 100
     
    int main(int argc, char *argv[])
    {
           struct sockaddr_in sin;
           char buf[MAX_LINE];
           int s_fd;
           int port = 8000;
           char *str = "test string";
           int n;
           if(argc > 1)
           {
                  str = argv[1];
           }
           bzero(&sin, sizeof(sin));
           sin.sin_family = AF_INET;
           inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr);
           sin.sin_port = htons(port);
           if((s_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
           {
                  perror("fail to creat socket");
                  exit(1);
           }
           if(connect(s_fd, (struct sockaddr *) &sin, sizeof(sin)) == -1)
           {
                  perror("fail to connect");
                  exit(1);
           }
           n = my_write(s_fd, str, strlen(str) + 1);
           if(n == -1)
                  exit(1);
           n = my_read(s_fd, buf, MAX_LINE);
           if(n == -1)
                  exit(1);
           printf("receive from server: %s
    ", buf);
           if(close(s_fd) == -1)
           {
                  perror("fail to close");
                  exit(1);
           }
           return 0;
    }
  • 相关阅读:
    iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流
    iOS开发之窥探UICollectionViewController(二) --详解CollectionView各种回调
    iOS开发之窥探UICollectionViewController(一) -- Ready Your CollectionViewController
    iOS开发之SQLite--C语言接口规范(五)——iOS开发使用SQLite实例
    iOS开发之SQLite--C语言接口规范(四) —— Result Values From A Query
    iOS开发之SQLite--C语言接口规范(三)——Binding Values To Prepared Statements
    iOS开发之SQLite-C语言接口规范(二) —— Prepared Your SQL Statements
    iOS开发之SQLite-C语言接口规范(一)——Ready And Open Your SQLite
    iOS开发之ImageView复用实现图片无限轮播
    iOS开发之多图片无缝滚动组件封装与使用
  • 原文地址:https://www.cnblogs.com/Mr--Yang/p/6719898.html
Copyright © 2011-2022 走看看