• Linux下的C语言基础-4


    实现一个字符串拷贝

    main.c文件

     1 #include <stdio.h>    
     2 #include "str_cpy.h"
     3 
     4 int main(int argc, const char *argv[])
     5 {
     6     char c[128] = {0};
     7     char c1[128];
     8     char *s=c1;
     9     scanf("%[^
    ]",s);
    10     char *p = str_cpy(c,s);
    11 
    12     printf("c[128]从终端考贝的数据是:%s
    ",c);
    13     
    14     return 0;
    15 }
    View Code

    str_cpy.c文件

     1 #include "str_cpy.h"
     2 
     3 char *str_cpy(char *s,char *c)
     4 {
     5 
     6     char *p = s;
     7     char *q;
     8     int i=0;
     9     for(q=c;*q!='';q++)
    10     {
    11         *p=*q;
    12         p++;
    13 
    14     }
    15 
    16     return s;
    17 }
    View Code

    str_cpy.h

    1 #ifndef __STR_CPY_H
    2 #define __STR_CPY_H
    3 
    4 #include <stdio.h>
    5 
    6 char *str_cpy(char *s,char *c);
    7 
    8 #endif
    View Code

    将两个文件中的内容复制到第三个文件中

    2.fgets(buf,sizeof(buf),stdin);
        第三个stdin 参数代表 从终端获取数据 放入buf。

    1 #include <string.h>
    2 void *memset(void *s, int c, size_t n);
    3 memset(buf,0,sizeof(buf))
    4 功能:清空缓冲区
    5 参数:buf 要被清空的缓冲区
    6 0 代表清空写入的数据
    7 sizeof(buf)代表清空长度
    8 返回值:无
      1 #include <stdio.h>    
      2 #include <string.h>
      3 
      4 int main(int argc, const char *argv[])
      5 {
      6     FILE *fp,*fp2;
      7     char buf[1024]={0};
      8     
      9     fp = fopen("./file.txt","w+");//打开文件1
     10     if(fp==NULL){
     11         
     12         perror("fopen
    ");
     13         return -1;
     14     }
     15     
     16     char *ret_s = fgets(buf,sizeof(buf),stdin);//从中端获取数据放入buf
     17     if(ret_s==NULL){
     18         
     19         perror("fgets
    ");
     20         return -1;
     21     }
     22     
     23     int ret = fputs(buf,fp);//将buf数据写入文件1
     24     if(ret<0){
     25         
     26         perror("fputs
    ");
     27         return -1;
     28     }
     29     
     30     fclose(fp);//关闭文件1
     31 
     32     memset(buf,0,sizeof(buf));//清空buf
     33 
     34     fp2 = fopen("./file2.txt","w+");//打开文件2
     35     if(fp2==NULL){
     36         
     37         perror("fopen
    ");
     38         return -1;
     39     }
     40     
     41     ret_s = fgets(buf,sizeof(buf),stdin);//从终端获取数据放入buf
     42     if(ret_s==NULL){
     43         
     44         perror("fgets
    ");
     45         return -1;
     46     }
     47     
     48     ret = fputs(buf,fp2);//将buf数据写入文件2
     49     if(ret<0){
     50         
     51         perror("fputs
    ");
     52         return -1;
     53     }
     54     
     55     fclose(fp2);//关闭文件2
     56 
     57     memset(buf,0,sizeof(buf));
     58 
     59     fp = fopen("./file.txt","r");// r方式打开文件1
     60     if(fp==NULL){
     61         
     62         perror("fopen
    ");
     63         return -1;
     64     }
     65     
     66     fp2 = fopen("./file3.txt","w");//w方式打开文件3
     67     if(fp2==NULL){
     68         
     69         perror("fopen
    ");
     70         return -1;
     71     }
     72     
     73     ret_s = fgets(buf,sizeof(buf),fp);//从文件1中将数据读到buf
     74     if(ret_s==NULL){
     75         
     76         perror("fgets
    ");
     77         return -1;
     78     }
     79     
     80     ret = fputs(buf,fp2);//将buf数据写入到文件3
     81     if(ret<0){
     82         
     83         perror("fputs
    ");
     84         return -1;
     85     }
     86     
     87     memset(buf,0,sizeof(buf));//清空buf
     88     
     89     fclose(fp);//关闭文件1
     90     fclose(fp2);//关闭文件3
     91 
     92     fp = fopen("./file2.txt","r");//r方式打开文件2
     93     if(fp==NULL){
     94         
     95         perror("fopen
    ");
     96         return -1;
     97     }
     98     
     99     fp2 = fopen("./file3.txt","a");//a方式打开文件3
    100     if(fp2==NULL){
    101         
    102         perror("fopen
    ");
    103         return -1;
    104     }
    105     
    106     ret_s = fgets(buf,sizeof(buf),fp);//将文件2中的数据读到buf
    107     if(ret_s==NULL){
    108         
    109         perror("fgets
    ");
    110         return -1;
    111     }
    112     
    113     ret = fputs(buf,fp2);//将buf数据写入文件3
    114     if(ret<0){
    115         
    116         perror("fputs
    ");
    117         return -1;
    118     }
    119     
    120     
    121     fclose(fp);//关闭文件2
    122     fclose(fp2);//关闭文件3
    123     
    124     memset(buf,0,sizeof(buf));//清空buf
    125 
    126 
    127     return 0;
    128 }
    View Code
     1 网络基础:
     2         IP:在网络中是主机的唯一标识 IP地址表示方法点分十进制 192.168.1.16
     3         IP组成:网络号(主机所在的网络)192.168.1.0+主机号  
     4         192.168.1.16表示在192.168.1.0网络中的16
     5         IP地址用32bit组成
     6         
     7         
     8         网络分类:
     9                 A:第一字节表示网络号,其余三个字节表示主机号
    10                     网络号字节最高位为0:0000 0000
    11                                         0111 1111  数据范围0-127
    12                     
    13                 B:前两个字节表示网络号,后两个字节表示主机号
    14                     网络号最高两位为10:1000 0000 
    15                                        1011 1111
    16                                        128-191
    17                 C:我们所处的类别中,前三字节表示网络号,后一字节表示我们的主机号
    18                                     192-233
    19                 D:组播
    20                 E:未使用  备用
    21 大小端:大端是指数据高字节保存在低地址中
    22         小端是指高字节保存在高地址中
    23         
    24 子网掩码:为了有效利用我们的IP衍生的
    25           子网号 = 主机号&子网掩码
     1 端口号:为了区分一台主机接收到的数据包应该交给哪个进程来处理,使用端口号来区分
     2 
     3 TCP:三次握手
     4     数据没传输前的处理:
     5     
     6     发送端端口              目的地端口
     7                     序号
     8                    确认号
     9     数据偏移 保留 UGR ACK PSH RST SYN FIN    窗口
    10     序号
    11         seq:占4个字节用来标记数据段顺序。tcp把连接中要发送的数据字节都编上号
    12          第一个字节编号由主机随机产生。给字节编号后给每个报文段指派一个序号;
    13          seq就是报文段中的第一个字节序号
    14     
    15     确认号:
    16             ack占4个字节,期待收到对方下一个报文段的第一个字节序号,
    17             序号表示报文段数据携带的数据字节第一编号;确认号是指期望收到下一字节的编号
    18             因此报文段最后一个字节+1  表示确认
    19             
    20                 ACK 占一个位,仅当ACK=1 确认号字段才有效。ACK=0 确认号无效
    21                 
    22     同步:
    23         SNY  连接建立时用于同步序号,当SYN=1 ACK=0,表示连接请求的报文段
    24         若同意连接,则响应报文段中将 SYN=1, ACK=1.
    25         因此 SYN=1表示这是一个请求,或连接接受报文
    26         SYN这个标志只有在TCP建立连接时才会被置1.  连接成功后(握手成功后)SYN=0
    27         
    28     终止:
    29         FIN 用来释放一个连接 FIN=1表示此报文段的发送方的数据已发送完毕,并且要求释放连接
    30 
    31     PS:ACK、SYN、FIN大写的单词的表示标志位 其值要么是0 要么是1  
    32         seq ack 表示序号
    33         
    34 第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,
    35             并进入SYN_SENT状态,等待服务器确认;
    36             SYN:同步序列编号(Synchronize Sequence Numbers)。
    37 
    38 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),
    39             同时自己也发送一个SYN包(syn=y),即SYN+ACK包,
    40             此时服务器进入SYN_RECV状态;
    41 
    42 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),
    43             此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
    44         
    45 作业:思考    为什么不能用两次握手进行连接?
    46         答:首先3次握手完成两个重要功能,双方都做好发送数据的准备工作(都知道彼此准备好了)
    47             也要允许双方初始序列号进行协商,这个序列号在握手时被发送确认。
    48             
     1 TCP四次挥手:
     2         1.客户端主动发出发送FIN=1 seq=u的释放报文给服务器进入FIN_WAIT 1状态
     3         
     4         2.服务器接收到客户端发来的释放报文后进入CLOSE_WAIT状态并向客户端发送
     5             seq=y,ack=u+1,ACK=1的释放确认报文并进入CLOSE_WAIT装态
     6             客户端接收到服务器发送来说确认报文后,进入FIN_WAIT 2状态
     7             发送完后会发送一些数据  (并不重要)给客户端
     8         
     9         3.当服务器发送完数据后再次向客户端FIN=1,ACK=1, seq=w ,ack =u+1
    10             确认释放报文,服务器进入LAST_ACK状态
    11         
    12         4.客户端接收到服务器发送的释放连接的报文后进入TIME_WAIT状态并发送
    13             
    14             ACK=1 seq=u+1,ack=w+1 服务器收到客户端的恢复后进入
    15             CLOSE 状态彻底断开连接
    16             客户端等待2MSL时间后也进入到CLOSE状态
    17             
    18 思考 为什么TCP连接需要三次握手,而断开需要四次挥手
    19     
    20     因为当服务器接收到客户端的SYN的连接请求报文后,会直接发送SYN+ACK这样一个应答报文
    21     SYN报文是用来同步客户端与服务器的标志。
    22     但是关闭时服务器收到FIN释放报文,不会立即关闭SOCKET(课后可以自行查阅资料或linux下用man)
    23     查看相应函数意义),所以服务器先回复一个ACK确认报文,告诉客户端 “你发的释放报文我收到了”
    24     等到服务器把所有与客户端要通信的报文发送完毕。客户端才能发送FIN(释放报文)。
    25     因此不能一起发送。所以需要四步挥手才能断开连接。
    26     
    27 思考 如果客户端和服务器建立了连接,由于种种原因导致客户端出现故障服务器端会如何处理
    28     客户端如果出现故障不能与服务器进行通信,服务器不会一直等下去的,因为一直等会白白
    29     浪费我们的资源。服务器每收到一次客户端的请求后都会重新为服务器内的一个计时器进行重置
    30     (一般我们服务器的计时器计时长度为2小时)。如果客户端2个小时没有与服务器发生数据通讯,
    31     服务器会每隔75秒发送一次探测报文。若是连发10次仍没有反应,服务器就判定客户端出现故障,
    32     主动断开连接。

    2.相关函数

     1 int socket (int domain, int type, int protocol);
     2     功能:创建套接字
     3 
     4     参数: 
     5         domain 是地址族
     6             PF_INET  PF_INET6 (AF_INET AF_INET6)// internet 协议
     7             PF_UNIX PF_LOCAL  // unix internal协议
     8             PF_NETLINK    AF_NETLINK   //内核和用户通信
     9             PF_PACKET    AF_PACKET     //用户自己定义包
    10     
    11         type  // 套接字类型
    12             SOCK_STREAM   // 流式套接字
    13             SOCK_DGRAM    // 数据报套接字
    14             SOCK_RAW         //  原始套接字
    15     
    16         protocol 参数通常置为0
    17         
    18     返回值:socket文件描述符
    19     
    20 
    21     #include <arpa/inet.h>
    22     uint16_t htons(uint16_t hostshort);
    23     功能:将主机字节序转换为网络字节序
    24     参数:要被转换的主机地址(uint16_t 无符号短整型)
    25     返回值:转换完的网络字节序
    26 
    27 
    28 
    29     #include <sys/socket.h>
    30     #include <netinet/in.h>
    31     #include <arpa/inet.h>
    32     in_addr_t inet_addr(const char *cp);
    33     功能:转换主机IP地址,将点分十进制转换成二进制
    34     参数: 要转换的IP地址
    35     返回值: 返回二进制的32位的地址。如果字符串包含的不是合法 的IP地址,则函数返回-1
    36     
    37     填充的结构体(相关信息):
    38     struct sockaddr_in {
    39     __kernel_sa_family_t  sin_family; /* Address family 协议族      */
    40     __be16        sin_port;   /* Port number         端口号 */
    41     struct in_addr    sin_addr;   /* Internet address    IP地址 */
    42 
    43     /* Pad to size of `struct sockaddr'. */
    44     unsigned char     __pad[__SOCK_SIZE__ - sizeof(short int) -
    45             sizeof(unsigned short int) - sizeof(struct in_addr)];
    46     };     
    47     
    48     #include <sys/types.h>  
    49     #include <sys/socket.h>
    50     int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);//
    51     功能:连接服务器函数
    52     参数:sockfd : socket返回的文件描述符
    53           serv_addr : 服务器端的地址信息
    54           addrlen : serv_addr的长度 
    55     返回值:返回值:成功0,失败-1
    56     
    57     #include <string.h>
    58     void *memset(void *s, int c, size_t n);
    59     功能: 清空缓冲区
    60     参数: s被清空缓冲区的首地址
    61             c清空写入的值
    62             n清空的长度
    63     返回值: 无
    64     
    65     
    66     #include <sys/socket.h>
    67     ssize_t    send(int socket,    const void*buffer, size_t length, int flags);
    68     功能:发送数据 
    69     参数:socke :对应的网络套接字文件描述符
    70           buffer : 发送缓冲区首地址
    71           length : 发送的字节数
    72           flags : 发送方式(通常为0)
    73  
    74     返回值:成功:实际发送的字节数,失败:-1, 并设置errno
    75     
    76     
    77     #include <sys/socket.h>
    78     ssize_t    recv(int socket, const void*buffer, size_t length, int flags);
    79     功能:接受网络数据
    80     参数:socke :对应的网络套接字文件描述符
    81           buffer : 发送缓冲区首地址
    82           length : 发送的字节数
    83           flags : 接收方式(通常为0)
    84  
    85     返回值:成功:实际接收的字节数,失败:-1, 并设置errno

    3.实现客户端

     1 #include <stdio.h>    
     2 #include <sys/types.h>          
     3 #include <sys/socket.h>
     4 #include <arpa/inet.h>
     5 #include <stdlib.h>
     6 #include <netinet/in.h>
     7 #include <arpa/inet.h>
     8 #include <string.h>
     9 
    10 #define LEN 4096
    11 
    12 
    13 int main(int argc, const char *argv[])
    14 {
    15     if(argc<3){
    16 
    17         printf("请输入服务器IP xxx.xxx.xxx.xxx地址和对应端口号:8888
    ");
    18         return -1;
    19     }
    20 
    21     const char *IP = argv[1];//终端获取的第二个参数
    22     const char *por = argv[2];//第三个参数
    23 
    24     char buf[LEN];//接收数据和发送数据的缓冲区
    25 
    26     struct sockaddr_in serveraddr;//定义一个sockaddr_in结构体变量
    27     
    28     int len = sizeof(serveraddr);//获取结构体长度
    29 
    30     int sfd = socket(AF_INET,SOCK_STREAM,0); //创建socket 获取文件描述符
    31     if(sfd<0){
    32         perror("创建socket失败
    ");
    33         return -1;
    34     }
    35 
    36     printf("socket 文件描述符是:%d
    ",sfd);
    37 
    38     serveraddr.sin_family = AF_INET;//填充网络协议
    39     serveraddr.sin_port = htons(atoi(por));//填充端口号
    40     serveraddr.sin_addr.s_addr = inet_addr(IP);//填充IP地址
    41     
    42     int ret = connect(sfd,(struct sockaddr *)&serveraddr,len);//链接服务器
    43     if(ret<0){
    44 
    45         perror("链接服务器失败
    ");
    46         return -1;
    47     
    48     }else{
    49     
    50         printf("您登录服务器成功
    ");
    51     }
    52     while(1){
    53         
    54         memset(buf,0,LEN);//清理缓冲区
    55 
    56         fgets(buf,LEN,stdin);//从终端获取数据 放入buf
    57 
    58         send(sfd,buf,strlen(buf),0);//向服务器发送指令数据
    59 
    60         memset(buf,0,LEN);//清理缓冲区
    61 
    62         recv(sfd,buf,LEN,0);//接受服务器返回的执行状态
    63 
    64         printf("%s
    ",buf);
    65     }
    66 
    67     return 0;
    68 
    69 }
    View Code
  • 相关阅读:
    字符串中的不可见字符应该如何清除?
    字符/字段数据的合并
    分割字符串的应用
    几种分割字符串实现方法的比较
    linux的一些文件基本命令
    centos7安装es6.4.0
    Sql 语句中 IN 和 EXISTS 的区别及应用
    Springboot通过redisTemplate实现发布订阅
    代理模式
    单例模式的多种实现方法
  • 原文地址:https://www.cnblogs.com/Halo-zyh-Go/p/12638968.html
Copyright © 2020-2023  润新知