华清实训学习 DAY5-6

第五天

文件IO

5、lseek : lseek - 重新定位读/写文件偏移
lseek() 重新定位与关联的打开文件描述的文件偏移量, 根据指令将文件描述符 fd 指向参数偏移量
存在库:
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
fd:文件描述符号
offset:偏移字节数
whence:偏移的位置
SEEK_SET:   从文件头部开始偏移offset个字节。
SEEK_CUR:   从文件当前读写的指针位置开始,增加offset个字节的偏移量。
SEEK_END:   文件偏移量设置为文件的大小加上偏移量字节。
返回值:成功返回当前指针距离文件的开始的字节数,失败返回-1;
示例:
自写代码(已标准化):
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFFER_SIZE 1024
#define SRC_FILE_NAME "src_file"
#define DEST_FILE_NAME "dest_file" 
//根据传入的参数来设置offset
#define OFFSET (atoi(args[1]))
int main(int argc, char*args[])
{
    int src_file, dest_file;
    unsigned char buff[BUFFER_SIZE];
    int real_read_len, off_set;
    if (argc != 2)
       { 
            fprintf(stderr, "Usage: %s offset\n", args[0]);
            exit(-1);
        }
      src_file = open(SRC_FILE_NAME, O_RDONLY);
      dest_file = open(DEST_FILE_NAME, O_WRONLY | O_CREAT, S_IREAD | S_IWRITE );        //owner权限:rw
            if (src_file < 0 || dest_file < 0)
                {
                    fprintf(stderr, "Open file error!\n");
                    exit(1);
                }
        off_set = lseek(src_file, -OFFSET, SEEK_END);
        //注意,这里对offset取了相反数 
    printf("lseek() reposisiton the file offset of src_file: %d\n", off_set);
    while((real_read_len = read(src_file, buff, sizeof(buff))) > 0)
        {
            write(dest_file, buff, real_read_len);
        }
    close(dest_file);
    close(src_file);
    return 0;
}
网络编程
1、服务器:接受连接请求,提供服务
2、客户端:发起连接请求
tcp/ip                         mqtt
tcp/ip:有连接的,可靠的,数据以字节流形式发送。
TCP 用于从应用程序到网络的数据传输控制。
TCP 负责在数据传送之前将它们分割为 IP 包,然后在它们到达的时候将它们重组。
ip:区分不同主机的依据
搭建tcp服务器
1、创建并打开套接字socket
2、绑定IP地址和端口号bind
3、创建监听队列listen
4、等待并建立连接accept
5、接收数据recv
6、关闭套接字close
1 、socket(获取套接字并设置属性)
socket() 创建一个通信端点并返回一个文件;
描述符指的是该端点。
成功调用返回的文件描述符将是当前未为进程打开的最小编号的文件描述符。
(man ip第七页有socket传参方式)
所在库:
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
格式:
int socket(int domain, int type, int protocol);
domain: 领域
type  :类型
protocol  :协议
socket()  creates  an  endpoint  for  communication and returns a file descriptor that  refers to that endpoint.  The file descriptor returned by a successful  call  will  be  the lowest-numbered file descriptor not currently open for the process.
socket() 创建通信端点并返回引用该端点的文件描述符。成功调用返回的文件描述符将是当前未为进程打开的最小编号的文件描述符。
The domain argument specifies a communication domain; this selects the protocol family  which will be used for communication.
域参数指定通信域;这选择协议族, 将用于通信。
RETURN VALUE
        On success, a file descriptor for the new socket is returned.  On  error,  -1  is  re turned, and errno is set appropriately.
    成功后,将返回新套接字的文件描述符。出错时,返回 -1,并适当设置 errno。
2、bind(绑定设置服务器的端口号)
   当使用 socket(2) 创建套接字时,它存在于名称空间(地址族)中 但没有为其分配地址。
bind()将addr指定的地址分配给 文件描述符 sockfd 引用的套接字。
addrlen指定大小,in addr 指向的地址结构的字节数。传统上,此操作 称为“为套接字分配名称”。
涉及库:
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
格式:
 int bind(int sockfd, const struct sockaddr *addr,  socklen_t addrlen);
课堂代码:
这段代码要通过两个终端页面执行:
服务器:
hqyj@ubuntu:~/desktop/s$ cat -n a.c
     1    #include<stdio.h>
     2    #include<unistd.h>
     3    #include<sys/types.h>
     4    #include<sys/socket.h>
     5    #include<netinet/in.h>
     6    #include<netinet/ip.h>
     7    #include<arpa/inet.h>               //引入必要的头文件,包括标准输入输出库、UNIX socket 库和网络地址库。
     8    
     9    
    10    int main()
    11    {
    12        //创建套接字  在 main()函数中,调用socket()函数创建一个新的套接字,并将其保存在sockfd变量中。
    13        int sockfd = socket(AF_INET,SOCK_STREAM,0); 
    14        if(0>sockfd)    //检查socket()函数的返回值,如果小于0,则表示创建套接字失败.程序将输出一个错误消息并退出。
    15        {
    16        perror("socket");
    17        return -1;
    18        }          //如果 socket() 函数成功返回,程序将输出套接字的文件描述符(即 sockfd 变量的值)。
    19         printf("sockfd:%d\n",sockfd);   
    20       //2 bind     创建一个 sockaddr_in 结构体,并填充它的成员变量以指定要绑定的本地 IP 地址和端口号。
    21         struct sockaddr_in addr;
    22        addr.sin_family = AF_INET;          //调用 bind()函数将套接字绑定到指定的本地 IP 地址和端口号上
    23        addr.sin_port = htons(8888);                            //将主机序转化为网络字节序(htons函数)
    24      addr.sin_addr.s_addr = inet_addr("127.0.0.1");   //将点分式Ip地址(字符串)转化成unsigned int型
                   //检查 bind() 函数的返回值,如果小于 0,则表示绑定失败。程序将输出一个错误消息并退出。
    25      if(0>bind(sockfd,(struct sockaddr *)&addr,sizeof(addr)))
    26      {
    27        perror("bind");
    28        return -1;
    29      }
    30      //打印ip地址 已存储于addr中,并且被转化成整形,用函数反向转化
    31      printf("ip:%s\n",inet_ntoa(addr.sin_addr));
    32      printf("端口号(port):%d\n",ntohs(addr.sin_port));
    33      
    34    
    35      //监听模式 listen调用 listen() 函数开始监听来自客户端的连接请求
    36      if(0 >listen(sockfd,5))
    37      {
    38        perror("listen");
    39        return -1;
    40      }
    41      printf("listen success\n");
    42     
    43        //4.accept 接受请求   会将程序中断在此,直到有服务器连接
    44        struct sockaddr_in client;
    45        int len =sizeof(client); 
                 // 调用 accept() 函数接受客户端的连接请求,并返回一个新的套接字。
    46       int clientfd = accept(sockfd,(struct sockaddr *)&client,&len);
    47       if( 0 > clientfd)         //如果accept()函数成功返回,程序将输出客户端的 IP 地址和端口号。
    48       {
    49        perror("accept");
    50        return -1;
    51       }
    52       printf("连接入的ip:%s\n连接端口号:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
    53       
    54       close(sockfd);        //调用 close() 函数关闭套接字,释放资源。
    55       close(clientfd);
    56       return 0;
    57    }
客户端:
hqyj@ubuntu:~/desktop/s$ cat c.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
int main()
{
    //创建打开套接字socket
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(0>sockfd)
    {
    perror("socket");
    return -1;
    }
     printf("sockfd:%d\n",sockfd);
   //2 bind
     struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(8888);                //将主机序转化为网络字节序
  addr.sin_addr.s_addr = inet_addr("127.0.0.1");       //将点分式Ip地址(字符串)转化成unsigned int型
  if(0>connect(sockfd,(struct sockaddr *)&addr,sizeof(addr)))
  {
    perror("connect");
    return -1;
  }
   while(1);
   close(sockfd);           //调用 close() 函数关闭套接字,释放资源。
   return 0;
}
运行结果:先运行服务器,再运行客户端代码
服务器:
hqyj@ubuntu:~/desktop/s$ ./a.out
sockfd:3
ip:127.0.0.1
端口号(port):8888
listen success
连接入的ip:127.0.0.1
连接端口号:43938
客户端:
hqyj@ubuntu:~/desktop/s$ ./c.out
sockfd:3
解读:
1. 引入必要的头文件,包括标准输入输出库、UNIX socket 库和网络地址库。
2. 在 main() 函数中,调用 socket() 函数创建一个新的套接字,并将其保存在 sockfd 变量中。
3. 检查 socket() 函数的返回值,如果小于 0,则表示创建套接字失败。程序将输出一个错误消息并退出。
4. 如果 socket() 函数成功返回,程序将输出套接字的文件描述符(即 sockfd 变量的值)。
5. 创建一个 sockaddr_in 结构体,并填充它的成员变量以指定要绑定的本地 IP 地址和端口号。
6. 调用 bind() 函数将套接字绑定到指定的本地 IP 地址和端口号上。
7. 检查 bind() 函数的返回值,如果小于 0,则表示绑定失败。程序将输出一个错误消息并退出。
8. 如果 bind() 函数成功返回,程序将输出绑定的 IP 地址和端口号。
9. 调用 listen() 函数开始监听来自客户端的连接请求。
10. 检查 listen() 函数的返回值,如果小于 0,则表示监听失败。程序将输出一个错误消息并退出。
11. 如果 listen() 函数成功返回,程序将输出一个成功监听的消息。
12. 调用 accept() 函数接受客户端的连接请求,并返回一个新的套接字。
13. 检查 accept() 函数的返回值,如果小于 0,则表示接受连接请求失败。程序将输出一个错误消息并退出。
14. 如果 accept() 函数成功返回,程序将输出客户端的 IP 地址和端口号。
15. 调用 close() 函数关闭套接字,释放资源。
3.listen(监听客户端的连接请求)
Listen() 将 sockfd 引用的套接字标记为被动套接字,即作为 将用于使用accept(2) 接受传入连接请求的套接字。
存在库:
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
格式:
 int listen(int sockfd, int backlog);
sockfd:新套接字的文件描述符
backlog:客户端最大连接数
4.accept(接受客户端的连接请求)
accept() 系统调用与基于连接的套接字类型(SOCK_STREAM、 SOCK_SEQPACKET)。它提取侦听套接字 sockfd 的挂起连接队列上的第一个连接请求,创建一个新的已连接套接字,并返回引用该套接字的新文件描述符。新创建的socket不处于监听状态。原来的套接字sockfd不受此调用的影响。
所在库
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
格式
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
The argument sockfd is a socket that has been created with socket(2), bound to a local  address with bind(2), and is listening for connections after a listen(2).
参数 sockfd 是一个使用 socket(2) 创建的套接字,使用 bind(2) 绑定到本地地址,并在listen(2)之后监听连接。
The argument addr is a pointer to a sockaddr structure.  This structure is  filled  in  with  the address of the peer socket, as known to the communications layer.   The exact  format of the address returned addr is determined by the socket's address  family  (see  socket(2)  and  the  respective  protocol  man  pages).  When addr is NULL, nothing is  filled in; in this case, addrlen is not used, and should also be NULL.
参数 addr 是指向 sockaddr 结构的指针。该结构填充有对等套接字的地址,如通信层所知。返回的地址 addr 的确切格式由套接字的地址族确定(请参阅套接字(2)和相应的协议手册页)。当addr为NULL时,不填任何内容;在这种情况下,addrlen 不被使用,并且也应该为 NULL。
The addrlen argument is a value-result argument: the caller must initialize it to con tain  the  size (in bytes) of the structure pointed to by addr; on return it will con tain the actual size of the peer address.
addrlen 参数是一个值-结果参数:调用者必须将其初始化以包含 addr 指向的结构的大小(以字节为单位);返回时它将包含对等地址的实际大小。
The returned address is truncated if the buffer provided is too small; in  this  case,addrlen will return a value greater than was supplied to the call.
如果提供的缓冲区太小,返回的地址将被截断;在这种情况下,addrlen 将返回一个大于提供给调用的值。
If  no  pending  connections are present on the queue, and the socket is not marked as  nonblocking, accept() blocks the caller until a connection is present.  If the  socket  is  marked  nonblocking  and no pending connections are present on the queue, accept()  fails with the error EAGAIN or EWOULDBLOCK.
如果队列中不存在挂起的连接,并且套接字未标记为非阻塞,则accept() 会阻塞调用者,直到存在连接。如果套接字被标记为非阻塞并且队列中不存在挂起的连接,则accept() 失败并出现错误 EAGAIN 或 EWOULDBLOCK。
In order to be notified of incoming connections on a socket, you  can  use  select(2),  poll(2), or epoll(7). 
A readable event will be delivered when a new connection is at tempted and you may then call accept() to get a socket for that connection.   Alterna tively,  you can set the socket to deliver SIGIO when activity occurs on a socket; see  socket(7) for details.
为了获得套接字上传入连接的通知,可以使用 select(2)、poll(2) 或 epoll(7)。当尝试新连接时,将传递一个可读事件,然后您可以调用accept()来获取该连接的套接字。或者,您可以将套接字设置为在套接字上发生活动时传递 SIGIO;详细信息请参见套接字(7)。
RETURN VALUE(返回值)
       On success, these system calls return a nonnegative integer that is a file descriptor  for the accepted socket.  On error, -1 is returned, errno is  set  appropriately, and  addrlen is left unchanged.
成功时,这些系统调用返回一个非负整数,它是已接受套接字的文件描述符。出错时,返回 -1,适当设置 errno,并且 addrlen 保持不变。
5,数据接收
所在库:
#include <sys/types.h>
#include <sys/socket.h>
格式: recv(sockfd, buf, len, flags);
1:ssize_t recv(int sockfd, void *buf, size_t len, int flags);
2:ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
3:ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
RETURN VALUE
       These  calls  return the number of bytes received, or -1 if an error occurred.  In the  event of an error, errno is set to indicate the error.
       这些调用返回接收到的字节数,如果发生错误则返回 -1。如果发生错误,则会设置 errno 来指示错误。
       When a stream socket peer has performed an orderly shutdown, the return value will  be  0 (the traditional "end-of-file" return).
        当流套接字对等方执行有序关闭时,返回值将为 0(传统的“文件结束”返回)。
       Datagram sockets in various domains (e.g., the UNIX and Internet domains) permit zero length datagrams.  When such a datagram is received, the return value is 0.
       各种域(例如 UNIX 和 Internet 域)中的数据报套接字允许零长度数据报。当接收到这样的数据报时,返回值为0。
       The value 0 may also be returned if the requested number of bytes to  receive  from  a  stream socket was 0.
       如果从流套接字接收的请求字节数为 0,则也可能返回值 0。
5、read和write
同文件io
ead 用于服务器中时,当 read 返回 0 时,表示客户端已经退出
总合代码:实现客户端与服务器的输入数据通信
服务器:
hqyj@ubuntu:~/desktop/s$ cat -n a.c
     1    #include<stdio.h>
     2    #include<unistd.h>
     3    #include<sys/types.h>
     4    #include<sys/socket.h>
     5    #include<netinet/in.h>
     6    #include<netinet/ip.h>
     7    #include<arpa/inet.h>
     8    
     9    
    10    int main()
    11    {
    12        //给饭店起名字
    13        int sockfd = socket(AF_INET,SOCK_STREAM,0);
    14        if(0>sockfd)
    15        {
    16        perror("socket");
    17        return -1;
    18        }
    19         printf("sockfd:%d\n",sockfd);
    20       //2 bind
    21         struct sockaddr_in addr;
    22        addr.sin_family = AF_INET;
    23        addr.sin_port = htons(8888);                                //将主机序转化为网络字节序
    24      addr.sin_addr.s_addr = inet_addr("127.0.0.1");       //将点分式Ip地址(字符串)转化成unsigned int型
    25      if(0>bind(sockfd,(struct sockaddr *)&addr,sizeof(addr)))
    26      {
    27        perror("bind");
    28        return -1;
    29      }
    30      //打印ip地址 已存储于addr中,并且被转化成整形,用函数反向转化
    31      printf("ip:%s\n",inet_ntoa(addr.sin_addr));
    32      printf("端口号(port):%d\n",ntohs(addr.sin_port));
    33      
    34    
    35      //监听 listen
    36      if(0 >listen(sockfd,5))
    37      {
    38        perror("listen");
    39        return -1;
    40      }
    41      printf("listen success\n");
    42     
    43        //4.accept 接受请求
    44        struct sockaddr_in client;
    45        int len =sizeof(client);
    46       int clientfd = accept(sockfd,(struct sockaddr *)&client,&len);
    47       if( 0 > clientfd)
    48       {
    49        perror("accept");
    50        return -1;
    51       }
    52       printf("连接入的ip:%s\n连接端口号:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
    53       
    54        //发送数据
    55        while(1)
    56        {
    57            char buf[20]={0};
    58            gets(buf);
    59            write(clientfd,buf,sizeof(buf));
    60        }
    61       close(sockfd);
    62       close(clientfd);
    63       return 0;
    64    }
客户端:
hqyj@ubuntu:~/desktop/s$ cat -n c.c
     1    #include<stdio.h>
     2    #include<unistd.h>
     3    #include<sys/types.h>
     4    #include<sys/socket.h>
     5    #include<netinet/in.h>
     6    #include<netinet/ip.h>
     7    #include<arpa/inet.h>
     8    
     9    
    10    int main()
    11    {
    12        //给饭店起名字
    13        int sockfd = socket(AF_INET,SOCK_STREAM,0);
    14        if(0>sockfd)
    15        {
    16        perror("socket");
    17        return -1;
    18        }
    19         printf("sockfd:%d\n",sockfd);
    20       //2 bind
    21         struct sockaddr_in addr;
    22      addr.sin_family = AF_INET;
    23      addr.sin_port = htons(8888);                //将主机序转化为网络字节序
    24      addr.sin_addr.s_addr = inet_addr("127.0.0.1");//将点分式Ip地址(字符串)转化成unsigned int型
    25      if(0>connect(sockfd,(struct sockaddr *)&addr,sizeof(addr)))
    26      {
    27        perror("connect");
    28        return -1;
    29      }
    30       while(1)
    31       {
    32        char buf[20]={0};                           //服务器发多少就收到多少
    33        read(sockfd,buf,sizeof(buf));         //读满40才结束
    34        puts(buf);
    35       }
    36       close(sockfd);
    37       return 0;
    38    }
结果:
服务器端显示连接成功后,可以输入数据,客户端将收到数据
课后练习:
(已标准化)
服务器:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#define PORT_NUM 9999                   //端口号定义
#define BUF_SIZE 1024                         //定义数组格式
int main()
{
    // 创建套接字
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    
    // 设置服务器地址和端口
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT_NUM);
    server_addr.sin_addr.s_addr = INADDR_ANY;
    
    // 绑定服务器地址和端口
    bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    
    // 开始监听
    listen(server_fd, 1);        //客户端最大连接数为1
    
    // 接收客户端连接
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);
    
    // 接收客户端消息并打印  数据接收
    char buf[BUF_SIZE];
    ssize_t recv_size;
    while ((recv_size = recv(client_fd, buf, BUF_SIZE, 0)) > 0) {
        printf("Received message from client: %s", buf);
    }
    
    // 关闭连接
    close(client_fd);
    close(server_fd);
    
    return 0;
}
客户端:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#define SERVER_ADDR "127.0.0.1"
#define PORT_NUM 9999
#define BUF_SIZE 1024
int main()
{
    // 创建套接字
    int client_fd = socket(AF_INET, SOCK_STREAM, 0);
    
    // 设置服务器地址和端口
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT_NUM);
    server_addr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
    
    // 连接服务器
    connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    
    // 发送消息
    while(1)
        {
            char buf[BUF_SIZE] = {0};
            gets(buf);
            send(client_fd, buf, sizeof(buf), 0);
         };   
    // 关闭连接
    close(client_fd);
    
    return 0;
}
  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值