网络编程知识汇总

网络编程

1.历史

1968:阿帕网问世,,是internet的雏形,但是传输数据不能跨平台
​
1974:TCP协议出现,在TCP协议中没有对应得检测纠错功能
​
1983:TCP/IP协议:具有跨平台,能检测检错得功能

协议:事先约定好得一种规则

2.网络协议模型

2.1 OSI七层网络协议模型

应用层:用户能看到的内容
表示层:对数据进行加密,解析
会话层:建立通信节点
传输层:实现点到点得通信
网络层:路由寻址
数据链路层:将数据封装成对应得帧格式、纠错、流控
物理层:屏蔽硬件差异

2.2 TCP/IP四层协议模型

1.应用层:HTTP(超文本传输协议)、FTP(文件传输协议)、NSF
2.传输层:TCP协议、UDP协议
3.网络层:IP协议、ICMP协议、IGMP协议(组播、广播)
4.物理与网络接口层:以太网协议、ARP协议、RARP协议(MAC-->IP),ppp


TCP通信特点:

面向链接,是种安全可靠、有序的传输通信、保证传输数据的准确无误不丢失、不失序

应用场景:用户登录、传输重要文件

UDP通信特点:

无连接,是一种不安全可靠的传输方式,传输数据不能保证数据的准确性

应用场景:流媒体软件、大型的音视频传输




网络通信的封包和拆包

img

img

一、socket编程预备知识

1.IP地址:

标识网络地址

IPV4:  32bits、 4字节大小的数据    2^32
          表示方法:
                       点分十进制:“1.1.1.1” “192.168.2.14”
                      二进制:00000001 00000001 00000001 00000001
​
IPV6: 128bits、16字节        2^128

1.1 IP地址的转换函数

inet_aton     //点分式 —–>二进制         只能用于 IPV4
​
inet_addr
​
inet_pton                                                  适用IPV4  IPV6
---------------------------------------------------------------------------------------------------------
inet_ntoa     //二进制 -->点分式          

inet_aton()

点分式 ———> 二进制
头文件:
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
函数原型:
    int inet_aton(const char *cp, struct in_addr *inp);
{
    参数:
        cp:ip地址字符串
        *inp:
            typedef uint32_t in_addr_t;
​
              struct in_addr {
                 in_addr_t s_addr;
              };
}  
返回值:
    成功:1
    失败:0

例子:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
​
int main(int argc, char *argv[])
{
    struct in_addr addr;
    if(0 == inet_aton("192.168.23.128",&addr)){
            perror("inet_aton");
            exit(-1);
            }
    printf("%u\n",addr.s_addr);
    return 0;
} 


inet_ntoa()

二进制 --> 点分式
头文件:
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
函数原型:
    char *inet_ntoa(struct in_addr in);
{
    参数:
        结构体定义的地址;
}

例子:

int main(int argc, char *argv[])
{
    struct in_addr addr;
    if(0 == inet_aton("192.168.23.128",&addr)){
            perror("inet_aton");
            exit(-1);
            }
    printf("%u\n",addr.s_addr);

    printf("%u\n",inet_addr("192.168.23.128"));

    char *ptr = inet_ntoa(addr);
    printf("%s\n",ptr);
    return 0;
} 



2.端口号 —port

一个端口对应一个进程

网络中标识进程身份的

1.本质上是一个unisgned short类型的数据,取值范围0~65536

2.1~1023:系统端口,一般不使用

3.1024~5000:常用的应用接口

4.5001~65536:用户可以使用的端口



3.字节序

1.字节序又称主机字节序,是计算的机中多字节整型数据的存储方式
2.内存存储多字节整数的方法叫做主机字节序;
  方法:
   大端序:低地址存高字节,高地址存低字节(网络字节序)
   小端序:高地址存高字节,低地址存低字节(主机字节序)
注意:网络数据流采用的是大端序

4.套接字–socket

4.1特点:

本质是一个特殊的按顺序分配的最小的文件描述符

1.是一个编程接口
2.并不仅限于TCP/IP协议
3.面向链接(TCP)
4.无连接(UDP)

4.2类型:

流式套接字---SOCK_STREAM 特点:面向连接、安全可靠
数据报套接字---SOCK_DGRAM 特点:无连接、不安全可靠
原始套接字---SOCK_RAW  特点:访问较低层次的协议

5.TCP编程———————

步骤

5.1 创建套接字-sockeat()

头文件:
     #include <sys/types.h>      
     #include <sys/socket.h>
函数原型:
    int socket(int domain, int type, int protocol);
{
    参数:
        domain:地址族
              AF_INET      IPv4 协议
              AF_INET6     IPv6协议
        type:套接字类型
            SOCK_STREAM:流式套接字--唯一对应tcp通信
            SOCK_DGRAM :数据报套接字--唯一对应udp通信
            SOCK_RAW :原始套接字--
        protocol:默认为 0
} 
返回值:
    成功:套接字
    失败:-1

例子:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{

    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0){
        perror("socket");
        exit(-1);
    }
    printf("sockfd = %d\n",sockfd);
    return 0;
} 

5.2绑定ip地址和端口号bind()

头文件:
    #include <sys/types.h>          
    #include <sys/socket.h>
函数原型:
    int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
{
    参数:
        sockfd:创建的套接字 --socket的返回值
        *addr:服务器的地址结构首地址
      addrlen:服务器地址结构大小
}
返回值:
    成功: 0
    失败: -1


IPV4对应的地址结构:
    struct sockaddr_in {
               sa_family_t    sin_family; //地址协议族
               in_port_t      sin_port;   //端口号
               struct in_addr sin_addr;   //
           };
    struct in_addr {
               uint32_t       s_addr;     /* address in network byte order */
           };
        

例子:

   struct sockaddr_in {
     saddr.sin_family = AF_INET; //IPV4
     saddr.sin_port = 5002;
     saddr.sin_addr.s_addr = inet_addr("192.168.7.220");

5.3设置监听套接字 listen()

作用:指定监听上线数---同时允许多少客户端建立连接
头文件:
    #include <sys/types.h>          
    #include <sys/socket.h>
函数原型:
     int listen(int sockfd, int backlog);
{
    参数:
        sockfd:创建的套接字--socket的返回值
        backlog:
               排队建立3次握手的队列和刚刚建立3次握手的队列的连接数和
        
}
返回值:
    成功:0
    失败:-1

5.4等待客服端连接accept()

作用:接收连接请求
头文件:
    #include <sys/types.h>         
    #include <sys/socket.h>
函数原型:
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
{
    参数:
       sockfd:创建的套接字
         addr:客服端的地址结构首地址 --传出参数
      addrlen:客服端地址结构的长度的首地址 --传入传出参数
}
返回值:
    成功:返回全新的套接字--用于服务器的数据读写
    失败:-1
    


5.5 建立连接 –connect()

作用:等待客户端建立连接
头文件:
    #include <sys/types.h>
    #include <sys/socket.h>
函数原型:
    int connect(int sockfd, const struct sockaddr *addr,
      socklen_t addrlen);
{
    参数:
        socket:文件描述符
        addr:传入参数,指定服务器地址信息,含IP地址和端口号
        addrlen:传入参数,sizeof(addr)大小
}
返回值:
    成功:0
    失败:-1 设置错误号


代码:

服务器–tcp_serve.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> 
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>

#define SIZE 64
int main(int argc, char *argv[])
{
    char buf[64]={0};
    pid_t pid;
    int ret,i;

    //创建套接字
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0){
        perror("socket");
        exit(-1);
    }
    printf("sockfd = %d\n",sockfd);

     struct sockaddr_in saddr;//定义服务器地址结构
     saddr.sin_addr.s_addr = inet_addr("192.168.10.128"); //查看 ifconfig
     saddr.sin_family = AF_INET; //IPV4
     saddr.sin_port = htons(8888);  //任意的端口
     
     int s_len = sizeof(saddr); //计算服务器结构地址大小
     ret = bind(sockfd,(struct sockaddr*)&saddr,s_len); //绑定
     if(ret < 0){
        perror("bind");
        exit(-1);
    }
     printf("bind successful\n");

     ret = listen(sockfd,128); //监听套接字
     if(ret < 0){
         perror("listen");
         exit(-1);
     }
     printf("listen successful!\n");

     struct sockaddr_in caddr;
     memset(&caddr,0,sizeof(caddr));

     int c_len = sizeof(caddr);

     int connfd = accept(sockfd,(struct sockaddr*)&caddr,&c_len); 
     if(connfd < 0){
         perror("accept");
         exit(-1);
     }
     printf("successful!  ip: %s   port:%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
     
     while(1){
         memset(buf,0,sizeof(buf));
         ret = read(connfd,buf,64);
         if(ret < 0){
             perror("read");
             exit(-1);
         }else if(ret == 0){
             close(connfd);
             break;
         }
         printf("ret->%d buf->%s\n",ret,buf);
     }
         close(sockfd);
     return 0;
}



客服端–tcp_client

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> 
#include <arpa/inet.h>
#include <string.h>

#define SIZE 64
int main(int argc, char *argv[])
{
    //创建套接字
    int connfd = socket(AF_INET,SOCK_STREAM,0);
    if(connfd < 0){
        perror("socket");
        exit(-1);
    }
    printf("connfd = %d\n",connfd);

     struct sockaddr_in saddr;//定义服务器地址结构
     saddr.sin_addr.s_addr = inet_addr("192.168.10.128"); //查看 ifconfig
     saddr.sin_family = AF_INET; //IPV4
     saddr.sin_port = htons(8888);
     
     int s_len = sizeof(saddr); //计算服务器结构地址大小
     int ret = connect(connfd,(struct sockaddr*)&saddr,s_len); //绑定
     if(ret < 0){
        perror("connect");
        exit(-1);
    }
     printf("connect successful\n");

     char buf[SIZE]={0};
     while(1){
         printf("请输入\n");
         fgets(buf,sizeof(buf),stdin);
         buf[sizeof(buf)-1] = '\0';

         ret=write(connfd,buf,strlen(buf));
        if(ret < 0){
            perror("read");
            exit(-1);
           }
      }  
     close(connfd);
    return 0;
} 




※tcp三次握手

1.第一次握手:客户端发送一个带有SYN(待确认数据包)标志的数据包给服务器。客户端进入 SYN_SEND状态 (待发送)

2.第二次握手:服务器接收后回传一个带有SYN/ACK标志的数据包给客户端。服务器进入SYN_RECV状态 (待接收)

3.第三次握手:最后,客户端回传一个带有ACK标志的数据包给服务器端,建立TCP链接,进入ESTABLIAHEND状态

※tcp四次挥手

意义:当服务器收到客户端FIN报表时,不确认是否所有数据都发送给了客户端,所以服务器不会马上管理链接,会发送ACK,在发送FIN给客户端,之后关闭连接。
1.第一次挥手:客户端发送FIN,用来关闭客户端和服务端的数据传输,这个时候客户端进入结束等待状态。

2.第二次挥手:当服务器端收到FIN时,会发送给客户端一个AKN,这个时候服务器端进入关闭等待状态。

3.第三次挥手:服务器端会发送一个FIN,用来关闭服务器端和客户端的数据传输,这个时候服务器端会进入最后确认状态。

4.第四次挥手:当客户端收到FIN时会进入最后等待状态,同时给服务端发送一个AKN,这个时候服务端会进入关闭状态。完成四次挥手。




6.服务器模型—————

6.1循环服务器

概念:

指每个服务器端依次处理每个客户端,直到当前客户端的所有请求处理完毕,再处理下一个客户端

优点:

简单

缺点:

容易造成其他客户端等待时间过长的情况

6.2多进程并发服务器

概念:

服务器端采用多任务机制(多进程或多线程)分别为每个客户端创建一个任务来处理,极大的提高了服务器的并发能力

工作流程

1.服务器端父进程从连接请求队列中提取请求,建立连接并返回新的已连接套接字
2.服务器端父进程创建子进程为客户端服务,客户端关闭连接时,子进程结束
3.服务器端父进程关闭已连接套接字,返回步骤(1)

创建步骤:

pid_t pid;
int sockfd = socket();
bind() //绑定
listen() //监听
signal(17,func)
while(1)
{
    int connfd = accept()
    
    pid = fork()
    if(pid == 0)
    {
        while(1)
        {
            read/write
        }
    }
    close(connfd)
}
void func{
    wait(NULL)
}

代码:

server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>
#include <sys/wait.h>

void wait_child(int signo)
{
    while(waitpid(0,NULL,WNOHANG) > 0);
    return ;
}
int main(int argc, char *argv[])
{
    pid_t pid;
    char buf[64] = {0};
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0){
        perror("socket");
        exit(-1);
    }
    printf("sockfd:%d\n",sockfd);

    struct sockaddr_in saddr;
    bzero(&saddr,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(8888);
    saddr.sin_addr.s_addr = inet_addr("192.168.10.128");

    int s_len =sizeof(saddr);
    int ret = bind(sockfd,(struct sockaddr *)&saddr,s_len);
    if(ret < 0){
        perror("bind");
        exit(-1);
    }
    printf("bing successful\n");

    ret = listen(sockfd,128);
    if(ret < 0){
        perror("listen");
        exit(-1);
    }
    printf("listen .......\n");

    while(1){

        struct sockaddr_in caddr;
        int c_len = sizeof(caddr);
        int connfd = accept(sockfd,(struct sockaddr*)&caddr,&c_len);
        if(connfd < 0){
            perror("accept");
            exit(-1);
        }
        printf("accept succesufl\n");
        printf("client: IP-> %s,port->%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));

        pid = fork();
        if(pid < 0){
            perror("foek");
            exit(-1);
        }else if(pid == 0){
            close(sockfd);
            break;
        }else{
        close(connfd);
        signal(SIGCHLD,wait_child);
        }
    }
    if(pid == 0){
        while(1){
            int connfd;
            ret =  read(connfd,buf,sizeof(buf));
            if(ret == 0){
                close(connfd);
                return 0;
            }else if(ret == -1){
                perror("read error");
                exit(-1);
            }else{
                for(int i = 0;i<ret;i++)
                    buf[i] = toupper(buf[i]);
                    write(connfd,buf,ret);         
            }
        }
    }
    return 0;
} 



6.3多线程的并发服务器

创建步骤

int sockfd = socket();
bind();
listen();
int connfd;
pthread_t thread;
while(1)
{
 	connfd = accept();   
    pthread_create(&thread, NULL, func, &connfd);   
    //pthread_join(thread, NULL);
    pthread_detach(thread);
}

void *func(void *arg)
{
    int connfd = *(int *)arg;
    while(1)
    {
        read(connfd);
        printf("");
    }   
}

代码:

server.c

#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> 
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>
#include  <pthread.h>

void *func(void *arg);

int main(int argc, char *argv[])
{
    int sockfd =socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0){
        perror("socket");
        exit(-1);
    }
    printf("sockfd->%d\n",sockfd);

    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;;
    saddr.sin_port = htons(8888);
    saddr.sin_addr.s_addr = inet_addr("192.168.10.128");

    int s_len = sizeof(saddr);
    int ret = bind(sockfd,(struct sockaddr*)&saddr,s_len);
    if(ret < 0){
        perror("bind");
        exit(-1);
    }
    printf("bind successful!\n");

    ret = listen(sockfd,128);
    if(ret < 0){
        perror("listen");
        exit(-1);
    }
    printf("listen......\n");

    int connfd;
    char buf[128] = {0};
    pthread_t tid;
    while(1){
        struct sockaddr_in caddr;
        int c_len = sizeof(caddr);
        connfd = accept(sockfd,(struct sockaddr*)&caddr,&c_len);
        if(connfd < 0){
            perror("accept");
            exit(-1);
        }
        printf("client->IP:%s,client->port:%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
      } 
      
    tid = pthread_create(&tid,NULL,func,&connfd);
    if(tid < 0){
        perror("pthread_create");
        exit(-1);
    }

    if(0 != pthread_detach(tid)){
        perror("pthread_detach");
        exit(-1);
    }

    close(sockfd);

    return 0;
} 

void *func(void *arg)
{
    int connfd = *(int*)arg;
    while(1){
        char buf[128]={0};
        memset(buf,0,128);
        int ret = read(connfd,buf,128);
        if(ret < 0){
            perror("read");
            exit(-1);
        }
        if(ret == 0){
        printf("a client leave!\n");
        break;
        }
        printf("ret->%d buf->%s\n",ret,buf);
        
    }
    close(connfd);
}



7.UDP编程————————

传输层协议

特点

无连接,是一种不安全可靠(不保证数据传输的准确性、无误、不失序)的通信协议

工作流程

这里写图片描述

7.1 UDP的相关函数

sendto()

头文件:
     #include <sys/types.h>
     #include <sys/socket.h>
函数原型:
     ssize_t send(int sockfd, const void *buf, size_t len, int flags);

       ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
{
    参数:
        sockfd:数据报套接字
        buf:发送数据的缓冲区首地址
        len:请求发送的字节数
        flag:发送方式,默认填0
        dest_addr:接收方的地址结构首地址
        addrlen:地址结构的长度
}


recvfrom()

头文件:
    #include <sys/types.h>
    #include <sys/socket.h>
函数原型:
     ssize_t recv(int sockfd, void *buf, size_t len, int flags);

       ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
{
    参数:
        sockfd:数据报套接字
        buf:发送数据的缓冲区首地址
        len:请求发送的字节数
        flag:发送方式,默认填0
        src_addr:接收方的地址结构首地址
        addrlen:地址结构的长度的地址

}
    

代码:

服务器端

#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{

    //1.创建套接子
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(socket < 0){
        perror("socket");
        exit(-1);
    }

    //绑定IP
    struct sockaddr_in saddr,caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(8888);
    saddr.sin_addr.s_addr = htons(INADDR_ANY);

    int s_len = sizeof(saddr);

    int ret = bind(sockfd,(struct sockaddr*)&saddr,s_len);
    if(ret < 0){
        perror("bind");
        exit(-1);
    }
    printf("a new client ......\n");

    //发送接受数据
    memset(&caddr,0,sizeof(caddr));
    int c_len =sizeof(caddr);
    char buf[64] = {0};
    while(1){
        memset(buf,0,64);
    ret = recvfrom(sockfd,buf,64,0,(struct sockaddr*)&caddr,&c_len);
    if(ret < 0){
        perror("recvfrom");
        exit(-1);
    }
    printf("IP->%s port->%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
    printf("%s\n",buf);
    }   
    close(sockfd);
    return 0;

客户端

#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    //创建套接子
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd < 0){
        perror("socket");
        exit(-1);
    }

    //数据的发送
    struct sockaddr_in  caddr;
    caddr.sin_family = AF_INET;
    caddr.sin_port = htons(8888);
    caddr.sin_addr.s_addr = htons(INADDR_ANY);

    int c_len = sizeof(caddr);
    char buf[64]={0};
    int ret;
    while(1){
        printf("请给服务器发送消息\n");
        fgets(buf,sizeof(buf),stdin);
        ret = sendto(sockfd,buf,64,0,(struct sockaddr*)&caddr,c_len);
        if(ret < 0){
            perror("sendto");
            exit(-1);
        }       
          }
close(sockfd);
    return 0;
} 

练习:

回传时间应答

服务器端
   
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char *argv[])
{ 
	time_t t;
	//1、创建套接字
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
		exit(-1);
	}
	
	//2、绑定IP地址和端口号
	struct sockaddr_in saddr, caddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(8888);
	saddr.sin_addr.s_addr = htons(INADDR_ANY);
	
	int s_len = sizeof(saddr);
	
	int ret = bind(sockfd, (struct sockaddr*)&saddr, s_len);
	if(ret < 0)
	{
		perror("bind");
		exit(-1);
	}
	
	//3、发送接收数据

	memset(&caddr, 0, sizeof(caddr));
	int c_len = sizeof(caddr);
	char buf[64] = {0};	
	
	while(1)
	{	
		memset(buf, 0, 64);
		ret = recvfrom(sockfd, buf, 64, 0, (struct sockaddr*)&caddr, &c_len);
		if(ret < 0)
		{
			perror("recvfrom");
			exit(-1);
		}
		//printf("ip -- %s, port -- %d: %s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buf);
		if(strncmp(buf, "time", 4) == 0)
		{
			memset(buf, 0, 64);
			time(&t);
			strcpy(buf, ctime(&t));
			
			sendto(sockfd, buf, 64, 0, (struct sockaddr*)&caddr, c_len);
		}
		else
		{
			memset(buf, 0, 64);			
			strcpy(buf, "input error! plz input <'time'>");
			sendto(sockfd, buf, 64, 0, (struct sockaddr *)&caddr, c_len);
		}
	}
	
	//4、关闭套接字
	close(sockfd);

    return 0;
} 
-----------------------------------------------------
客户端
    
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{ 
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
		exit(-1);
	}
	
	struct sockaddr_in saddr, caddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(8888);
	saddr.sin_addr.s_addr = inet_addr("192.168.7.216");
	
	int s_len = sizeof(saddr);
	int c_len = sizeof(caddr);
	
	char buf[64] = {0};
	fgets(buf, 64, stdin);
	
	int ret = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&saddr, s_len);
	if(ret < 0)
	{
		perror("sendto");
		exit(-1);
	}
	memset(buf, 0, 64);
	ret = recvfrom(sockfd, buf, 64, 0, (struct sockaddr*)&caddr, &c_len);
	if(ret < 0)
	{
		perror("recvfrom");
		exit(-1);
	}
	printf("recv:%s\n", buf);	
		
	close(sockfd);
		
    return 0;
} 





8.IO模型—————————

1.阻塞IO

读阻塞:当进行读操作没有相关资源,程序阻塞,当有资源的时候,程序继续执行

写阻塞:当写缓冲区满时,会出现写阻塞,当缓冲区有足够空间时候,程序继续执行

缺点:阻塞IO会阻碍其他程序的执行

2.非阻塞IO

fcntl()

设置描述符的属性

头文件:
    #include <unistd.h>
函数原型:
    int fcntl(int fd, int cmd, ... /* arg */ );
{
    参数:
        fd:文件描述符
        cmd:命令
            F_GETFL:获取指定文件描述符的属性
            F_SETFL:设置指定文件描述符的属性
        arg:
            取决于cmd,如果是 F_GETFL,arg可忽略
                如果是F_SETFL,arg表示设置属性
}
返回值:
    成功:
    失败:-1
    
例:
   		1、获取的对应文件描述符的属性
   			int flag = fcntl(fd, F_GETFL, 0);
   		2、改变状态标志
   			flag |= O_NONBLOCK;  //将该文件描述符的属性设置为非阻塞
   		3、设置对应文件描述符的属性
   			fcntl(fd, F_SETFL, flag);

缺点:轮询进行遍历,消耗系统资源

代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{ 
    int fd = open("/dev/input/mice", O_RDWR);
    if(fd < 0)
    {
        perror("open");
        return -1;
    }

    char buf[64] = {0};
    char buf1[64] = {0};

    int flag = fcntl(0, F_GETFL, 0);
    flag |= O_NONBLOCK;
    fcntl(0, F_SETFL, flag);
    /*
    flag = fcntl(fd, F_GETFL, 0);
    flag |= O_NONBLOCK;
    fcntl(fd, F_SETFL, flag);
    */
    while(1)
    {
        fgets(buf, 64, stdin);
        printf("buf = %s\n", buf);

        read(fd, buf1, 64);
        printf("%d -- %d -- %d\n", buf1[0], buf1[1], buf1[2]);
    }

    return 0;
}




3.IO多路复用

IO多路复用可以同时监控多个文件描述符,找到其中“活动的”描述符,所谓活动的就是指可以操作读写错误异常,实现IO多路复用

3.1 select()

1.准备好要监控的描述符

2.将要监控的描述符放到描述符集合中
    void FD_CLR(int fd, fd_set *set);
      功能:从指定检测表中,删除某个文件描述符
    int  FD_ISSET(int fd, fd_set *set);
      功能:判断指定的文件描述符是否有响应
    void FD_SET(int fd, fd_set *set);
      功能:从指定的检测表中,添加某个文件描述符
    void FD_ZERO(fd_set *set);
       功能:清空描述符集合
       
3.调用select函数集合
 函数原型:
           int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
{
    参数:
        int nfds:所有检测表中,文件描述符最大+1
        fd_set *readfds:读检测表的首地址(是否可读)
        fd_set *writefds:写检测表的首地址(是否可写)
        fd_set *exceptfds:异常检测表的首地址(是否异常)
        struct timeval *timeout:设置超时检测的时间(不超时传NULL)
              struct timeval{
                long tv_sec;
                long tv_usec;
             }
}
返回值:
    成功:返回活动的描述符个数 
    超时:返回 0
    出错:返回 -1
    
4.处理活动的描述符

缺点:

​        select机制检测的文件描述符的上线为1024个

​         select机制中初始和返回表是分离的

代码1:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> 
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>
#include <sys/select.h>
#include <sys/time.h>


#define SIZE 64
int main(int argc, char *argv[])
{
    char buf[1000]={0};
    int ret;

    //创建套接字
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0){
        perror("socket");
        exit(-1);
    }
    printf("sockfd = %d\n",sockfd);

     struct sockaddr_in saddr;//定义服务器地址结构
     saddr.sin_addr.s_addr = inet_addr("192.168.10.128"); //查看 ifconfig
     saddr.sin_family = AF_INET; //IPV4
     saddr.sin_port = htons(8888);  //任意的端口
     
     int s_len = sizeof(saddr); //计算服务器结构地址大小
     ret = bind(sockfd,(struct sockaddr*)&saddr,s_len); //绑定
     if(ret < 0){
        perror("bind");
        exit(-1);
    }
     printf("bind successful\n");

     ret = listen(sockfd,128); //监听套接字
     if(ret < 0){
         perror("listen");
         exit(-1);
     }
     printf("listen successful!\n");

     //准备描述符集合
     fd_set set;
     int *fds = malloc(1000*sizeof(int));
     //设置所有的值为-1
     memset(fds,-1,1000*sizeof(int));
     int maxfd; //最大的描述符
     int i;

     while(1){
         maxfd = sockfd;
         FD_ZERO(&set);
         FD_SET(sockfd,&set); //将监听的描述符加入set

         //连接上客户端的描述符加入set
         for(i=0;i<1000;i++){
             if(fds[i] != -1){
                 FD_SET(fds[i],&set);
                 //记录最大的描述符
                 if(fds[i] > maxfd){
                     maxfd = fds[i];
                 }
             }
             //select
             if(select(maxfd+1,&set,NULL,NULL,NULL)<=0){
                 printf("error....\n");
                 continue;
             }
         }
         
         //处理活动的描述符
         //有客户端连上来
         if(FD_ISSET(sockfd,&set)){
             struct sockaddr_in client;
             socklen_t len = sizeof(client);

          int newfd = accept(sockfd,(struct sockaddr*)&client,&len); 
             if(newfd < 0){
              perror("accept");
              continue;
             }

             //将新连接上来的描述符加入到fds中
             for(i=0;i<1000;i++){
                 if(fds[i] == -1)
                     break;
             }
             fds[i]=newfd;
             printf("%s到此一遇\n",inet_ntoa(client.sin_addr));
         }

         //有客户端发消息
         for(i=0;i<1000;i++){
             if(fds[i] != -1 && FD_ISSET(fds[i],&set)){
                 ret =read(fds[i],buf,sizeof(buf));
                 if(ret <= 0){
                    close(fds[i]);
                    fds[i] = -1;
                    continue;
                 }
                 printf("%s\n",buf);
                 if(strcmp(buf,"quit") == 0){
                     close(fds[i]);
                     fds[i] = -1;
                     continue;
                 }
                 //原路发回
                 write(fds[i],buf,ret);
             }
         }
     }
     close(sockfd);
     return 0;
}

代码2:

服务器
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/time.h>


int main(int argc, char *argv[])
{

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
        perror("socket");
        exit(-1);
    }

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(8888);
    //saddr.sin_addr.s_addr = inet_addr("192.168.7.220");
	saddr.sin_addr.s_addr = htons(INADDR_ANY);

    int s_len = sizeof(saddr);

    int ret = bind(sockfd, (struct sockaddr*)&saddr, s_len);
    if(ret < 0)
    {
        perror("bind");
        exit(-1);
    }

    ret = listen(sockfd, 5);
    if(ret < 0)
    {
        perror("listen");
        exit(-1);
    }

    struct sockaddr_in caddr;
    memset(&caddr, 0, sizeof(caddr));

    int c_len = sizeof(caddr);
    
    char buf[64] = {0};
	fd_set rfds, tmp;  //创建读监测表
	
	FD_ZERO(&rfds);   //将读监测表的每一位清0
	FD_SET(sockfd, &rfds);  //将套接字加入读监测表
	int maxfd = sockfd+1; //获取文件描述符最大值+1
	int connfd = -1;	

    while(1)
    {
        //printf("wait for a new client...\n");
        tmp = rfds;

        ret = select(maxfd, &tmp, NULL, NULL, NULL);  //监测所有表
        if(ret < 0)
        {
        	perror("select");
        	exit(-1);
        }
        for(int i = 0; i < maxfd; i++)  //循环遍历所有表
        {
        	if(FD_ISSET(i, &tmp))  //判断是否有文件描述符响应
        	{
        		if(i == sockfd)   //判断响应的文件描述符是否为sockfd
        		{
         			connfd = accept(i, (struct sockaddr *)&caddr, &c_len);
       				if(connfd < 0)
        			{
            			perror("connect");
            			exit(-1);
        			}
        			printf("link successful! ip -- %s  port -- %d \n", inet_ntoa(caddr.sin_addr),  ntohs(caddr.sin_port)); 
        			FD_SET(connfd, &rfds); // 将accept得到的新的套接字添加到读监测表中    			
        			(connfd+1) > maxfd ? (maxfd = maxfd+1):(maxfd = maxfd); //改变maxfd的值
        		}
        		else if(i > sockfd)
        		{
                		memset(buf, 0, 64);
                		ret = read(i, buf, 64);
                		if(ret < 0)
                		{
                    		perror("read");
                    		exit(-1);
                		}
                		if(ret == 0)
                		{
                   			 printf("a client leave!\n");
                   			 close(i);
                   			 FD_CLR(i, &rfds); //将关闭的文件描述符从读监测表中删除
                    		 break;
                		}
                		printf("recv %d:%s\n", ret, buf);          	    			
        		}
        	}
        }       
    }
    close(sockfd);
    return 0;
} 

3.2 poll()

头文件:
     #include <poll.h>
函数原型:
     int poll(struct pollfd *fds, nfds_t nfds, int timeout);
{
    参数:
        struct pollfd *fds:本质是数组,检测文件的最大个数
        
}

注意:select函数与poll函数的区别是,前者底层是数组,所以有最大连接数的限制,后者是链表,无最大连接数的限制)

练习:

poll实现并发服务器

server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/select.h>
#include <poll.h>

#define SIZE 64

int server_init(char *ipaddr, unsigned short port, int backlog)
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建网络通信接口
    if(-1 == sockfd)
    {
        perror("socket");
        return -1;
    }

    //printf("sockfd=%d\n",sockfd);

    
    struct sockaddr_in saddr;//服务器的地址结构
    bzero(&saddr,sizeof(saddr));//memset()
    saddr.sin_family = AF_INET;//指定协议族ipv4 
    saddr.sin_port = htons(port);//端口号:5001~65535 
    saddr.sin_addr.s_addr = (ipaddr == NULL)? htonl(INADDR_ANY) : inet_addr(ipaddr);//ip地址 点分式 -> 二进制网络字节序


    int ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));//绑定 将服务器的ip和port与sockfd绑定
    if(-1 == ret)
    {
        perror("bind");
        close(sockfd);
        return -1;
    }
    //printf("bind success\n");

    ret = listen(sockfd, backlog);//监听是否有客户端请求连接 sockfd:监听套接字
    if(-1 == ret)
    {
        perror("listen");
        close(sockfd);
        return -1;
    }

    return sockfd;
}

//设置非阻塞
int set_nonblock(int fd)
{
    int flags = fcntl(fd, F_GETFL);//获取fd的状态标志位
    if(-1 == flags)
    {
        perror("fcntl");
        return -1;
    }

    flags |= O_NONBLOCK;//设置非阻塞状态

    return (fcntl(fd, F_SETFL, flags));
}

int main(int argc, char *argv[])
{
    if(argc < 3)
    {
        printf("Usage:%s <ip> <port>\n",argv[0]);
        return -1;
    }
    int sockfd = server_init(argv[1], atoi(argv[2]), 1024);
    if(-1 == sockfd)
    {
        printf("server_init error\n");
        return -1;
    }
    printf("listen....\n");

    struct sockaddr_in caddr;//保存客户端的信息(ip port protocol)
    bzero(&caddr, sizeof(caddr));
    socklen_t clen = sizeof(caddr);

    //rws:用于服务器和连接的客户端通信
    int rws = accept(sockfd, NULL, NULL);//阻塞等待客户端请求连接
    if(-1 == rws)
    {
        perror("accept");
        close(sockfd);
        return -1;
    }

    printf("rws=%d\n",rws);

    //通信
    char buf[SIZE] = {0};
    struct pollfd fd[1024] = {0};//定义文件描述符集合

    //将0和rws文件描述符添加到fd中
    
    fd[0].fd = 0;
    fd[0].events = POLLIN;//监测读事件

    fd[1].fd = rws;
    fd[1].events = POLLIN;//监测读事件

    int nfds = 2;//监测的文件描述符的个数
    int ret;

    while(1)
    {
        ret = poll(fd,nfds,-1);//监测是否有文件描述符准备就绪
        if(-1 == ret)
        {
            perror("poll");
            break;
        }
        else if(0 == ret)
        {
            printf("timeout....\n");
            continue;
        }
        
        if(fd[0].revents & POLLIN)//判断读标准输入是否准备就绪
        {
            bzero(buf,sizeof(buf));
            fgets(buf,sizeof(buf),stdin);
            //回传消息给客户端
            ret = send(rws,buf,sizeof(buf),0);
            if(-1 == ret)
            {
                perror("send");
                break;
            }

        }
        if(fd[1].revents & POLLIN)//rws准备就绪
        {
            bzero(buf, sizeof(buf));
            int ret = recv(rws,buf,sizeof(buf),0);//读客户端消息
            if(-1 == ret)
            {
                perror("recv");
                close(rws);
                fd[1].fd = -1;
                break;
            }
            else if(0 == ret)//表示客户端关闭
            {
                printf("client closed\n");
                close(rws);
                fd[1].fd = -1;
                break;
            }
            puts(buf);
        }
    }
    close(sockfd);
    return 0;
} 

client.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>

#define SIZE 64

int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建网络通信接口
    if(-1 == sockfd)
    {
        perror("socket");
        return -1;
    }

    printf("sockfd=%d\n",sockfd);

    
    struct sockaddr_in saddr;//服务器的地址结构
    bzero(&saddr,sizeof(saddr));//memset()
    saddr.sin_family = AF_INET;//指定协议族ipv4 
    saddr.sin_port = htons(8888);//端口号:5001~65535 
    saddr.sin_addr.s_addr = inet_addr("192.168.1.12");//ip地址 点分式 -> 二进制网络字节序


    int ret = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));//绑定 将服务器的ip和port与sockfd绑定
    if(-1 == ret)
    {
        perror("connect");
        close(sockfd);
        return -1;
    }
    printf("connect success\n");

    
    struct pollfd fd[1024] = {0};//定义文件描述符集合

    //将0和sockfd文件描述符添加到fd中
    
    fd[0].fd = 0;
    fd[0].events = POLLIN;//监测读事件

    fd[1].fd = sockfd;
    fd[1].events = POLLIN;//监测读事件

    int nfds = 2;//监测的文件描述符的个数


    //通信
    char buf[SIZE] = {0};

    do{
        ret = poll(fd,nfds,-1);//监测是否有文件描述符准备就绪
        if(-1 == ret)
        {
            perror("poll");
            break;
        }
        else if(0 == ret)
        {
            printf("timeout....\n");
            continue;
        }

        if(fd[0].revents & POLLIN)//0准备就绪
        {
            bzero(buf,sizeof(buf));

            fgets(buf,sizeof(buf),stdin);//标准输入获取字符串
            ret = write(sockfd,buf,sizeof(buf));//给服务器写请求
            if(-1 == ret)
            {
                perror("write");
                break;
            }
        }
        if(fd[1].revents & POLLIN)//sockfd可读
        {
            bzero(buf,sizeof(buf));
            ret = read(sockfd, buf, sizeof(buf));//读取服务器回传的内容
            if(-1 == ret)
            {
                perror("read");
                break;
            }
            puts(buf);
        }
    }while(strncmp(buf,"quit",4) != 0);


    close(sockfd);

    return 0;
} 

3.3epoll()

适用于linux

※epoll_create()

头文件:
    #include <sys/epoll.h>
函数原型:
    int epoll_create(int size);
{
    参数:
        size:
             一般填1
}
返回值:
    成功:返回一个操作epoll文件描述符表的句柄(文件描述符)
    失败:返回-1

※epoll_ctl()

头文件:
    #include <sys/epoll.h>
函数原型:
   int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
{
    参数:
        epfd:
   		操作文件描述符表的相关句柄
   	     op:
		    EPOLL_CTL_ADD:添加事件
		    EPOLL_CTL_DEL:删除事件
         fd:
            相关的文件描述符
	    event:操作事件的首地址(文件描述符中某个操作元素的首地址)  
    
	typedef union epoll_data {
		void        *ptr;
		int          fd;
		uint32_t     u32;
		uint64_t     u64;
	} epoll_data_t;

	struct epoll_event {
		uint32_t     events;      /* Epoll events */
		epoll_data_t data;        /* User data variable */
	};	

※检测epoll_wait()

#include <sys/epoll.h>

   int epoll_wait(int epfd, struct epoll_event *events,
                  int maxevents, int timeout);
{
    参数:
   	   epfd:操作文件描述符相关句柄
   	   events:文件描述符表的首地址
   	   maxevents:监测事件的总项数
       timeout:用于超时检测
}

代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/epoll.h>

int main(int argc, char *argv[])
{ 
	int fd1 = open("/dev/input/mice", O_RDONLY);
	
	char buf1[64] = {0};
	char buf2[64] = {0};
	
    int epfd = epoll_create(2);

	struct epoll_event pfd[1000];
	for(int i = 0; i < 1000; i++)
	{
		pfd[i].data.fd = -1;
	}
	
	int pos = -1;
	
	pfd[++pos].events = EPOLLIN;
	pfd[pos].data.fd = 0;
    epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &pfd[pos]);
	
	pfd[++pos].events = EPOLLIN;
	pfd[pos].data.fd = fd1;
    epoll_ctl(epfd, EPOLL_CTL_ADD, fd1, &pfd[pos]);
	
	int ret;
	while(1)
	{
		ret = epoll_wait(epfd, pfd, pos+1, -1);
		if(ret < 0)
		{
			perror("poll");
			exit(-1);
		}
		for(int i = 0; i <= pos; i++)
		{
			if(pfd[i].events & EPOLLIN)
			{
				if(pfd[i].data.fd == 0)
				{
					fgets(buf1, 64, stdin);
					printf("buf1 = %s\n", buf1);
				}
				else if(pfd[i].data.fd == fd1)
				{			
					read(fd1, buf2, 64);
					printf("%d -- %d -- %d\n", buf2[0], buf2[1], buf2[2]);
				}	
			}
		}	
	}	
    return 0;
} 



9.设置套接字属性

设置setsockopt()

头文件:
    #include <sys/types.h>
    #include <sys/socket.h>
函数原型:
    int setsockopt(int sockfd, int level, int optname, const void * optval,
socklen_t optlen);
{
    参数:
        sockfd:套接字
        level:设置属性层
             SOL_SOCKET:通用套接字层
             IPPROTO_IP:IP层
             IPPRO_TCP:TCP层
        optname:指定操作(宏)
        optval:1
        optlen: 指针指向无法存取的内存空间.
        
}
返回值:
    成功:则返回 0, 
    失败:则返回-1, 错误原因存于 errno.


获取getsockopt()

头文件:
    #include <sys/types.h>
    #include <sys/socket.h>
函数原型:
    int getsockopt(int sockfd, int level, int optname, const void * optval,
socklen_t optlen);
{
    参数:
        sockfd:套接字
        level:设置属性层
             SOL_SOCKET:通用套接字层
             IPPROTO_IP:IP层
             IPPRO_TCP:TCP层
        optname:指定操作(宏)
        optval:1
        optlen: 指针指向无法存取的内存空间.
        
}
返回值:
    成功:则返回 0, 
    失败:则返回-1, 错误原因存于 errno.

※检测超时检测的三种方法

1.信号量 捕捉信号 signal
2.io多路复用
3.设置套接字属性


10.广播和组播(多播)—UDP

1.ip地址分类

A类地址:
     最高位为0,主机号占24位
     地址范围:1.0.0.1~126.255.255.254
  
B类地址:
      最高位为10,主机号占16位
      地址范围:128.0.0.1~191.254.255.254
    
C类地址:
      最高位为110,主机号占8位
      地址范围:192.0.1.1~123.255.254.254
    
D类地址:(又称为组播地址)
最高位为1110
      地址范围:224.0.0.1~239.255.255.254

E类地址保留

2.广播

应用:投屏软件,ip获取

	服务器:

1、创建套接字 -- socket();

2、绑定IP地址和端口号 -- bind();

while(1)

{

3、发送、接收数据 -- sendto()/recvfrom();

}

4、关闭套接字 -- close();					

客户端:

1、创建套接字 -- socket();

2、设置套接字属性,允许发送广播数据 -- setsockopt();

while(1)

{

3、向广播地址发送数据 -- sendto()/recvfrom();

}				

4、关闭套接字 -- close();		

3.组播

//服务器
1.创建套接字---socket
2.setsockopt()--将当前主机加入到组播IP组中去
3.将当前IP加入到指定的组播中 -- setsockopt()
4.绑定IP和端口号--bind
while(1)
{
    5.发送、接收数据---sendto,recvfrom
}
6.关闭套接字---close
    
//客户端
1.创建套接字---socket

3.while(1)
{
    3.发送、接收数据---sendto,recvfrom
}
4.关闭套接字---close
    struct ip_mreq  {
    		struct in_addr imr_multiaddr;  //组播IP 
   			struct in_addr imr_interface;  //本机IP
		};   

代码:

server.c

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{ 
	//1、创建套接字
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
		exit(-1);
	}
	
	struct ip_mreq ip_group;
	ip_group.imr_multiaddr.s_addr = inet_addr("239.10.0.1");
	ip_group.imr_interface.s_addr = inet_addr("192.168.7.216");
	
	int g_len = sizeof(ip_group);
	
	if(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &ip_group, g_len) < 0)
	{
		perror("setsockopt IP_ADD_MEMBERSHIP");
		exit(-1);
	}
	
	
	//2、绑定IP地址和端口号
	struct sockaddr_in saddr, caddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(8888);
	saddr.sin_addr.s_addr = htons(INADDR_ANY);
	
	int s_len = sizeof(saddr);
	
	int ret = bind(sockfd, (struct sockaddr*)&saddr, s_len);
	if(ret < 0)
	{
		perror("bind");
		exit(-1);
	}
	
	//3、发送接收数据

	memset(&caddr, 0, sizeof(caddr));
	int c_len = sizeof(caddr);
	char buf[64] = {0};	
	
	while(1)
	{	
		memset(buf, 0, 64);
		ret = recvfrom(sockfd, buf, 64, 0, (struct sockaddr*)&caddr, &c_len);
		if(ret < 0)
		{
			perror("recvfrom");
			exit(-1);
		}
		printf("ip -- %s, port -- %d: %s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buf);
	}
	
	//4、关闭套接字
	close(sockfd);

    return 0;
} 

client.c

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{ 
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
		exit(-1);
	}
	
	struct ip_mreq ip_group;
	ip_group.imr_multiaddr.s_addr = inet_addr("239.10.0.1");
	ip_group.imr_interface.s_addr = inet_addr("192.168.7.216");
	
	int g_len = sizeof(ip_group);
	
	if(setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &ip_group, g_len) < 0)
	{
		perror("setsockopt IP_MULTICAST_IF");
		exit(-1);
	}

	struct sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(8888);
	saddr.sin_addr.s_addr = inet_addr("239.10.0.1");
	
	int s_len = sizeof(saddr);
	
	char buf[64] = {0};
	fgets(buf, 64, stdin);
	
	int ret = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&saddr, s_len);
	if(ret < 0)
	{
		perror("sendto");
		exit(-1);
	}
	
	close(sockfd);
		
    return 0;
} 

3.域套接字

通信流程跟TCP通信一致

域套接字相关地址结构

 struct sockaddr_un {
           sa_family_t sun_family;               /* AF_UNIX */
           char        sun_path[108];            /* Pathname */
       };	

代码:

服务器

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/un.h>


int main(int argc, char *argv[])
{ 
	int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
		exit(-1);
	}
	
	struct sockaddr_un saddr;
	saddr.sun_family = AF_UNIX;
	strcpy(saddr.sun_path, "mysocket");
	
	int s_len = sizeof(saddr);
	
	int ret = bind(sockfd, (struct sockaddr*)&saddr, s_len);
	if(ret < 0)
	{
		perror("bind");
		exit(-1);
	}

	ret = listen(sockfd, 6);
	if(ret < 0)
	{
		perror("listen");
		exit(-1);
	}
	
	
	int connfd = accept(sockfd, NULL, NULL);
	if(connfd < 0)
	{
		perror("accept");
		exit(-1);
	}
	
	char buf[64] = {0};
	while(1)
	{
		memset(buf, 0 , 64);
		read(connfd, buf, 64);
		printf("buf:%s\n", buf);
	}
	
	close(connfd);
	close(sockfd);
    return 0;
} 

客户端

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/un.h>

int main(int argc, char *argv[])
{ 
	int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
		exit(-1);
	}
	
	struct sockaddr_un saddr;
	saddr.sun_family = AF_UNIX;
	strcpy(saddr.sun_path, "mysocket");
	
	int s_len = sizeof(saddr);
	
	int ret = connect(sockfd, (struct sockaddr*)&saddr, s_len);
	if(ret < 0)
	{
		perror("connect");
		exit(-1);
	}
	
	char buf[64] = {0};
	while(1)
	{
		fgets(buf, 64, stdin);
		write(sockfd, buf, strlen(buf));
	}
	
	close(sockfd);
    return 0;
} 



11.数据库

sqlite3

数据库名字以 .db 结尾

相关的基础命令

.help:查看帮助手册
.exit:退出
.tables:查看
.quit:退出 

sql语句

所有sql语句都要以 分号 结尾

创建表:
    create table <tablename>;

删除表:
    drop table <tablename>;
    
插入数据:
    insert into <tablename> values();

删除数据:
    delete from <tablename> where <info>=<information>;


查看数据:
    select *from <tablename>;

修改数据:
    update <tablename> set <info>=<information_new> where <info>=<information_old>;
    

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值