Linux C语言网络编程(学习笔记一):socket实现本地通信

2023-05-16

目录

        1.网络编程常识

        2.简单的本地通信

        2.1 socket实现本地通信

        2.2 相关API讲解

        2.3 服务端和客户端代码演示


        Linux的网络连接是通过内核完成的,其支持多种网络协议,如TCP/IP、IPX、DDP以及IPv6等。Linux系统通过提供套接字(scoket)进行网络编程。网络程序通过socket和其他几个函数调用后返回一个通信的文件描述符,可以将这个描述符看成普通文件的描述符来操作,并通过对描述符读写操作实现网络间的数据交流。

        1.网络编程常识

        TCP/IP协议是一组在网络中提供可靠数据传输和非可靠数据服务的协议。该协议组中最主要的协议就是TCP协议和IP协议,当然还包括其他协议,例如ICMP、ARP、PPP等协议。提供网络可靠传输(面向连接)的称为TCP协议,提供非可靠(面向无连接)传输的称为UDP

        TCP/IP协议参考模型如图所示。

TCP/IP参考模型及协议
应用层FTP,Telnet,HTTP
传输层TCP,UDP
网络互联层IP
主机联网层以太网令牌环网802.2HDLC,PPP,FRAME-RELAY
802.3EIA/TIA-232,V.35

        在TCP/IP参考模型中,去掉了OSI参考模型中会话层和表示层(这两层的功能被合并到应用层实现),同时将OSI参考模型中的数据链路层和物理层合并为主机联网层

        2.简单的本地通信

        使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的对等通信。

        socket进程通信与网络通信使用的是统一套接口,只是地址结构与某些参数不同。socket编程要考虑服务器端和客户端两方面内容,而客户端/服务器模式也是网络上绝大多数通信应用程序都使用的机制。客户服务器模式要求每个应用程序由两部分组成,一部分负责启动通信,另一部分负责应答。客户端和服务端关系如图。

        2.1 socket实现本地通信

        服务器端的编程步骤:

        (1)调用socket函数来创建一个socket描述符。

        (2)准备通信地址。

        (3)对通信地址和socket描述符进行绑定(使用bind函数)

        (4)读写数据(使用read和write函数实现)

        (5)关闭socket描述符(使用close函数)

         客户端的编程步骤:

         (1)调用socket函数来创建一个socket描述符。

         (2)准备通信地址

         (3)对通信地址和socket描述符进行连接(使用socket函数)

         (4)读写数据(使用read和write函数实现)

         (5)关闭socket描述符(使用close函数)

        我们发现,客户端和服务端除了第(3)步不同,其他步骤基本相同。

        2.2 相关API讲解

        下面详细介绍编程步骤里提到的各个函数以及通信地址(实质上是定义一个结构体类型的变量)的用法

        1.socket函数

        函数原型:int socket (int domain, int type, int protocol);

        参数domain的取值及作用

        AF_UNIX/AF_LOCAL/AF_FILE: 创建本地通信描述符。

        AF_INET:创建网络通信描述符IPV4。

        AF_INET6: 创建网络通信描述符IPV6。

        注:AF换成PF效果一样

        参数type的取值及作用

        SOCK_STREAM: 实现面向连接的通信类型,即基于TCP的通信。

        SOCK_DGRAM: 实现面向非连接的通信类型,即基于UDP的通信。

        参数protocol本来用于指定协议,但目前基本没有意义,所以赋值为0即可。

        返回值:成功则返回socket描述符(非负整数),若失败返回值为-1。

        2.通信需要准备的结构体

        struct sockaddr: 无实际意义的结构体,称之为“傀儡”。

        struct sockaddr_un: 表示负责本地通信的地址数据。

        struct sockaddr_in:表示负责网络通信的地址数据。

#include <sys/un.h>
#include <netinet/in.h>
struct sockaddr_un
{
        sa_family_t sun_family; // 用于指定协议簇,要和创建socket描述符步骤中socket函数参数domain的取值一致
        char sun_path[]; //存放socket文件名(只要是存在的一个文件或文件夹即可,一般情况下使用特殊目录.或者..)                                           
        
};
struct sockaddr_in
{
        sa_family_t sin_family; //  用于指定协议簇,要和创建socket描述符步骤中socket函数参数domain的取值一致
        short sin_port; //  端口号
        struct in_addr sin_addr; // 存储IP地址
        
};

                3.bind函数

                函数原型: int bind(int sockfd, struct sockadd *addr, socklen_size);

                参数说明

                sockfd:编程步骤(1)中使用socket函数返回的描述符。

                addr: 通信使用的地址,本质是sockaddr_in或sockaddr_un类型的数据,使用时将作类型转换,转换成struct sockaddr * 类型的数据。

                size: addr对应结构体的大小。

                返回值:成功则返回0,失败则返回-1。

                4.connect函数

                函数原型: int connect(SOCKET s,const struct sockaddr * name,int namelen);

                参数说明
                s: socket描述符。

                name: 指向要连接套接字的sockaddr结构体的指针。

                namelen: sockaddr结构体的字节长度。

                返回值:成功返回0,失败返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取响应错误代码。

                5.read和write函数

                函数原型:int read(int fd, char *buf,int len);

                                      int write(int fd, char *buf, int len);

                参数说明

                 fd指定读写操作的socket描述符。

                 buf在read函数中指定接收数据缓冲区,在write函数中表示发送数据缓冲区。

                 len 指定接收或发送的数据大小。

                 返回值:read函数执行成功后返回读到的数据大小,失败则返回-1;write函数执行成功后返回写入的数据大小,失败则返回-1;

                  以上就是关于socket实现本地通信所需要的函数。

                   用socket实现本地通信测试程序。

        2.3 服务端和客户端代码演示

       local_server.c服务器端程序代码如下:                                              

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
int main()
{
        /* 创建socket描述符 */
        int sockfd = socket(PF_UNIX,SOCK_DGRAM,0);  // UDP通信
        if(sockfd == -1)
        {
                perror("socket failed");
                exit(-1);
        }
        /* 准备地址 */
        struct sockaddr_un addr;
        addr.sun_family = PF_UNIX;
        strcpy(addr.sun_path, "a.sock");
        /* 绑定 */
        int res = bind(sockfd,(struct sockaddr *)&addr, sizeof(addr)); //做强制类型转换
        if(res == -1)
        {
                perror("bind failed!");
                exit(-1);
        }
        /* 进行通信 */
        char buf[100] = {0};
        int len = read(sockfd,buf,sizeof(buf));
        if(len < 0)
        {
                perror("read failed ");
                exit(-1);
        }
        printf("read from sockfd %s\n", buf);
        /* 关闭socket描述符 */
        close(sockfd);
        /* 删除a.sock*/
        unlink("a.sock");
        return 0;
}

        local_client.c客户端程序代码如下:                      

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
int main()
{
        /* 创建socket描述符 */
        int sockfd = socket(PF_UNIX,SOCK_DGRAM,0);
        if(sockfd == -1)
        {
                perror("socket failed ");
                exit(-1);
        }
        /* 准备地址 */
        struct sockaddr_un addr;
        addr.sun_family = PF_UNIX;
        strcpy(addr.sun_path,"a.sock");
        /* 进行连接 */
        int res = connect(sockfd,(struct sockaddr *)&addr,sizeof(addr));
        if(res == -1)
        {
                perror("connect failed!");
                exit(-1);
        }
        /* 进行通信 */
        int len = write(sockfd," hello socket",13);
        if(len < 0)
        {
                perror("write failed");
                exit(-1);
        }
        /* 关闭 */
        close(sockfd);
        /* 删除a.sock */
        unlink("a.sock");
        return 0;
}

        执行命令编译两个源程序

gcc local_server.c -o local_server
gcc local_client.c -o local_client

        先执行服务器端程序,再执行客户端程序,结果如下图

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux C语言网络编程(学习笔记一):socket实现本地通信 的相关文章

随机推荐