linux 网络编程基础---1

时间:2022-12-17 23:46:21
嵌入式Linux网络编程      1、掌握TCP/IP协议的基础知识      2、掌握嵌入式Linux基础网络编程      3、掌握嵌入式Linux高级网络编程      4、能够独立编写客户端、服务器端的通信程序  
 一、TCP/IP协议的基础知识       1、TCP协议分成了两个不同的协议:    (1)用来检测网络传输中差错的传输控制协议TCP    (2)专门负责对不同网络进行互联的互联网协议IP
    2、网络的体系结构     概念网络采用分而治之的方法设计,将网络的功能划分为不同的模块,以分层的形式有机组合在一起。     每层实现不同的功能,其内部实现方法对外部其他层次来说是透明的。每层向上层提供服务,同时使用下层提供的服务     网络体系结构即指网络的层次结构和每层所使用协议的集合。     常用体系结构有:     (1)OSI     (2)TCP/IP
    OSI参考模型及TCP/IP参考模型 
linux 网络编程基础---1  linux 网络编程基础---1linux 网络编程基础---1 linux 网络编程基础---1 linux 网络编程基础---1linux 网络编程基础---1
    3、TCP/IP协议族     常用协议
    TCP(Transport Control Protocol)传输控制协议    IP(Internetworking Protocol)网间协议    UDP(User Datagram Protocol)用户数据报协议    ICMP(Internet Control Message Protocol)互联网控制信息协议    SMTP(Simple Mail Transfer Protocol)简单邮件传输协议    SNMP(Simple Network manage Protocol)简单网络管理协议    HTTP(Hypertext Transfer Protocol) 超文本传输协议    FTP(File Transfer Protocol)文件传输协议    ARP(Address Resolution Protocol)地址解析协议
    4、TCP和UDP协议    TCP:是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信。    linux 网络编程基础---1linux 网络编程基础---1linux 网络编程基础---1
    适用情况:    (1)适合于对传输质量要求较高,以及传输大量数据的通信。    (2)在需要可靠数据传输的场合,通常使用TCP协议    (3)MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议
    UDP(User Datagram Protocol)用户数据报协议,是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。    适用情况:    (1)在接收到数据,给出应答较困难的网络中使用UDP。(如:无线网络)    (2)适合于广播/组播式通信中。    (3)MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通讯通常采用UDP协议    (4)流媒体、VOD、VoIP、IPTV等网络多媒体服务中通常采用UDP方式进行实时数据传输
    同时,一个UDP应用可同时作为应用的客户或服务器方。由于UDP协议并不需要建立一个明确的连接,因此建立UDP应用要比建立TCP应用简单得多。



二、网络基础编程
    1、网络编程常用函数关API        socket() 创建套接字    bind() 绑定本机地址和端口    connect() 建立连接    listen() 设置监听端口    accept() 接受TCP连接    recv(), read(), recvfrom() 数据接收    send(), write(), sendto() 数据发送    close(), 关闭套接字



    1、创建socket          #include <sys/types.h>          /* See NOTES */       #include <sys/socket.h>
       int socket(int domain, int type, int protocol);
         该函数用于建立一个socket连接,可指定socket类型等信息。在建立了socket连接之后,可对sockaddr或sockaddr_in结构进行初始化,以保存所建立的socket地址信息。
    2、绑定端口/IP        #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);

           bind():该函数是用于将本地IP地址绑定到端口号,若绑定其他IP地址则不能成功。另外,它主要用于TCP的连接,而在UDP的连接中则无必要。
    3、监听连接请求(非阻塞)       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       int listen(int sockfd, int backlog);
      listen():在服务端程序成功建立套接字和与地址进行绑定之后,还需要准备在该套接字上接收新的连接请求。此时调用listen()函数来创建一个等待队列,在其中存放未处理的客户端连接请求。

    4、连接请求(阻塞)        #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);
       connect():该函数在TCP中是用于bind()的之后的client端,用于与服务器端建立连接,如果服务端一直没有处理连接请求,那么函数将会阻塞,一直等待连接成功。。而在UDP中由于没有了bind()函数。         connect()有点类似bind()函数的作用。
    5、接收连接请求(阻塞)        #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <sys/socket.h>
       int accept4(int sockfd, struct sockaddr *addr,
                   socklen_t *addrlen, int flags);
        accept():服务端程序调用listen()函数创建等待队列之后,调用accept()函数等待并接收客户端的连接请求。    它通常从由bind()所创建的等待队列中取出第一个未处理的连接请求。
         6、发送与接收数据(阻塞)       #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);
       ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
         send()和recv():这两个函数分别用于发送和接收数据,可以用在TCP中,也可以用在UDP中。当用在UDP时,可以在connect()函数建立连接之后再用。
       #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);

       ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);


  sendto()和recvfrom():这两个函数的作用与send()和recv()函数类似,也可以用在TCP和UDP中。当用在TCP时,后面的几个与地址有关参数不起作用,函数作用等同于send()和recv();当用在UDP时,可以用在之前没有使用connect()的情况下,这两个函数可以自动寻找指定地址并进行连接。



/* function name : server.c */

#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netinet/in.h>

/* Macro Definition */
#define NET_PORT 7777
#define IP_ADDRESS "192.168.1.109"

/* Gobal Varialb */
pthread_t Pth_rd,Pth_wr;//定义读写线程id
int socket_fd,new_sockfd;//定义socket文件描述符

/*
 *    函数描述:接收数据线程
 */

void* pthrecv(void *arg)
{
    char buffer[100];
    while(1)    
    {
        bzero(buffer,sizeof(buffer));//清空缓冲区

        // 从new_socket读取信息,并存放在buffer中
        //若无数据发送,则此函数将会阻塞
        int num = recv(new_sockfd,buffer,sizeof(buffer), 0);
        buffer[num] = '\0';        //在接收数据追加'\0',防止输出乱码
        printf("Receive data is :%s\n",buffer);//打印接收数据 
    }
}

/*
 *    向socket写入数据(new_sockfd)
 */

void* pthwrite(void *arg)
{
    char buffer[100];
    while(1)
    {
        bzero(buffer,sizeof(buffer));  //清空缓冲区
        scanf("%s",buffer);  //获取用户输入
        send(new_sockfd,buffer,sizeof(buffer), 0); //发送数据(若无接收将引起阻塞)
    }
}

/*
*    函数描述:创建socket,并创建两个线程:一个接收数据,另外一个
*    获取用户输入,并发送数据。函数流程如下:
*    1.
*/

int main(void)
{
    /* 1. 创建 socket: 通信协议:IPV4,SOCK类型:TCP(流式套接字) */
    socket_fd = socket(AF_INET,SOCK_STREAM,0);
    if( socket_fd < 0)//创建失败
    {
        perror("socket create fail:");
        exit(-1);
    }

    /* 2. 绑定 socket 和 IP地址和网络端口号 */
    //初始化socket结构体以保存socket的信息
    struct sockaddr_in saddr1,new_sockaddr;

    memset(&saddr1 , 0 ,sizeof(saddr1));//清空saddr1 结构体
    saddr1.sin_family = AF_INET; // IPV4协议
    saddr1.sin_port = htons( NET_PORT );  //将主机字节序(端口号)转换为网络字节序
    saddr1.sin_addr.s_addr = inet_addr( IP_ADDRESS ); //IP地址

    int ret = bind( socket_fd, (struct sockaddr *)&saddr1,sizeof(saddr1));//绑定socket
    if(ret<0)
    {
        perror("bind fail !:");
        exit(-1);
    }

    /* 3. 监听连接:非阻塞 */
    //fd:监听连接套接字,5:指定等待连接的最大队列数(连接的请求数)
    ret = listen(socket_fd,5);
    if(ret<0//无请求
    {
        perror("listen fail:");
        exit(-1);
    }

    /* 4. 接受连接 */
    // 返回值为新创建的socket的套接字描述符
    int size = sizeof(struct sockaddr);
    new_sockfd = accept(socket_fd,
                (struct sockaddr *)&new_sockaddr,
                &size);

    /* 5. 发送/接收数据 (可阻塞)*/
    /* 创建接收数据线程 */
    ret = pthread_create(&Pth_rd,NULL,pthrecv,(void *)&socket_fd);//传递socketfd
    if(ret<0)
    {
        printf("Create Receive pthread Fail:");
        exit(-1);    
    }
    /* 创建发送数据线程 */
    ret = pthread_create(&Pth_wr,NULL,pthwrite,(void *)&socket_fd);//传递socketfd
    if( ret<0)
    {    
        printf("Create Write Pthread Fail:");
        exit(-1);    
    }

    pthread_join(Pth_rd,NULL); //等待接收线程结束
    pthread_join(Pth_wr,NULL); //等待发送线程结束

    /* 6. 关闭socket,new_socket 套接字 */
    close(socket_fd);
    close(new_sockfd);

}



/* function name : client.c */

#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netinet/in.h>
#include<pthread.h>
#include<strings.h>

/* Macro Definition */
#define NET_PORT 7777
#define IP_ADDRESS "192.168.1.109"

int socket_fd;

pthread_t Pth_rd,Pth_wr;//定义读写线程id

/*
 *    函数描述:接收数据线程
 */

void* pthrecv(void *arg)
{
    char buffer[100];
    while(1)
    {
        bzero(buffer,sizeof(buffer));//清空缓冲区
        
        // 从new_socket读取信息,并存放在buffer中
        //若无数据发送,则此函数将会阻塞
        int num = recv(socket_fd,buffer,sizeof(buffer), 0);
        buffer[num] = '\0';    //在接收数据追加'\0',防止输出乱码
        printf("Receive data is :%s\n",buffer); //打印接收数据
    }
}

/*
 *    向socket写入数据(new_sockfd)
 */


void* pthwrite(void *arg)
{
    char buffer[100];
    while(1)
    {
        bzero(buffer,sizeof(buffer)); //清空缓冲区
        scanf("%s",buffer); //获取用户输入
        send(socket_fd,buffer,sizeof(buffer), 0); //发送数据(若无接收将引起阻塞)
    }
}

/*
*    函数描述:创建socket,并创建两个线程:一个接收数据,另外一个
*    获取用户输入,并发送数据。函数流程如下:
*    1.
*/


int main(void)
{
    /* 1. 创建 socket: 通信协议:IPV4,SOCK类型:TCP(流式套接字) */
    socket_fd = socket(AF_INET,SOCK_STREAM,0);
    if( socket_fd < 0)//创建失败

    {
        perror("socket create fail:");
        exit(-1);
    }

    /* 2. 初始化socket结构体以保存socket的信息 */
    struct sockaddr_in saddr1;

    memset(&saddr1 , 0 ,sizeof(saddr1));//清空saddr1 结构体
    saddr1.sin_family = AF_INET; // IPV4协议
    saddr1.sin_port = htons(NET_PORT);  
    saddr1.sin_addr.s_addr = inet_addr(IP_ADDRESS);  //将主机字节序(端口号)转换为网络字节序

    /* 3. 连接服务器(非阻塞) */
    int ret =  connect(socket_fd,(struct sockaddr *)(&saddr1),sizeof(saddr1));
    if(ret<0)//连接失败
    {
        perror("Connect Fail!:");
    }
    else 
    {
        printf(" Connect Success !\n");
    }

    /* 4. 发送/接收数据 (可阻塞) */
    /* 创建接收数据线程 */
    ret = pthread_create(&Pth_rd,NULL,pthrecv,(void *)&socket_fd);
    if(ret<0)
    {
        printf(" Create Recv Pthread fail:");
        exit(-1);    
    }
    /* 创建发送数据线程 */
    ret = pthread_create(&Pth_wr,NULL,pthwrite,(void *)&socket_fd);
    if( ret<0)
    {    
        printf("Create Write Pth Fail:");
        exit(-1);    
    }

    pthread_join(Pth_rd,NULL); //等待接收线程结束
    pthread_join(Pth_wr,NULL); //等待发送线程结束    

    /* 5. 关闭socket,new_socket 套接字 */
    close(socket_fd);    

}