网络编程学习笔记一(网络基础+TCP编程+UDP编程)

目录

一、网络采用分层的思想:

二、各层典型的协议:

网络接口和物理层:

ARP/RARP :

PPP协议:

网络层:

传输层:

应用层: 

嵌入式相关:

三、 网络的封包和拆包

四、网络编程的预备知识

4.1 SOCKET

4.2 IP地址

4.3 端口号

4.4 字节序

五、TCP编程API 

1.socket()函数

1.1参数

1.2返回值:

2.bind()函数

2.1 参数:

3.listen()函数:

4.accept():

4.1 参数:

4.2 返回值:

5.客户端的连接函数 connect()

5.1 参数:

5.2 返回值:

6. 套接字的关闭close()

重大知识点:(项目代码改进需要)

创建套接字后,使用setsockopt函数对绑定地址快速重用就不会出现这个情况!!!! 

补充send()/recv()函数API 

网络接受数据:recv()/read() 

六、UDP编程API

发送数据sendto() :

接受数据recvfrom():

示例代码:

udp可以实现多个客户端对一个服务器 

七、TCP、IP协议原理

三次握手、四次挥手 

注意事项:

八、unix域套接字(unix domain)

用于本地进程间的通信

一、网络采用分层的思想:

        1.每一层实现不同的功能,对上层的数据做透明传输

        2.每一层向上层提供服务,同时使用下层提供的服务

二、各层典型的协议:

网络接口和物理层:

        MAC地址:48位全球第一,网络设备的身份标识

ARP/RARP :

        ARP: IP地址----->MAC地址

        RARP: MAC地址--->IP地址

PPP协议:

        拨号协议(GPRS/3G/4G)

网络层:

        IP地址

        IP: Internet protocol(分为IPV4和IPV6)

        ICMP: Internet控制管理协议,ping命令属于ICMP

        IGMP: Internet分组管理协议,广播、组播

传输层:

        TCP: (Transfer Control protocol,传输控制协议) 提供面向连接的,一对一的可靠数据传输的协议,即数据无误、数据无丢失、数据无失序、数据无重复到达的通信。
       UDP: (user Datagram Protocol, 用户数据报协议): 提供不可靠,无连接的尽力传输协议,是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。
        SCTP: 是可靠传输,是TCP的增强版,它能实现多主机、多链路的通信。

应用层: 

        网页访问协议:HTTP/HTTPS
        邮件发送接收协议: POP3(收)/SMTP(发) 、IMAP(可接收邮件的一部分)
        FTP,
        Telnet/SSH: 远程登录    

嵌入式相关:

        NTP: 网络时钟协议
        SNMP: 简单网络管理协议(实现对网络设备集中式管理)      
        RTP/RTSP:用传输音视频的协议(安防监控)

三、 网络的封包和拆包

四、网络编程的预备知识

4.1 SOCKET

        4.1.1 socket是一个应用编程的接口,它是一种特殊的文件描述符(对它执行IO的操作函数,比如,read(),write(),close()等操作函数)

        4.1.2 socket代表着网络编程的一种资源

        4.1.3 socket的类型:

流式套接字(SOCK_STREAM): 唯一对应着TCP
  提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。
数据报套接字(SOCK_DGRAM): 唯一对应着UDP
   提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。
原始套接字(SOCK_RAW):(对应着多个协议,发送穿透了传输层)
   可以对较低层次协议如IP、ICMP直接访问。

4.2 IP地址

        1.IP地址分为IPV4和IPV6
                IPV4:采用32位的整数来表示
                IPV6:采用了128位整数来表示
                mobileIPV6: local IP(本地注册的IP),roam IP(漫游IP)

        IPV4地址:
                点分形式: 192.168.7.246
                32位整数

        特殊IP地址:

                局域网IP: 192.XXX.XXX.XXX  10.XXX.XXX.XXX
                广播IP: xxx.xxx.xxx.255, 255.255.255.255(全网广播)
                组播IP: 224.XXX.XXX.XXX~239.xxx.xxx.xxx

4.3 端口号

        16位的数字(1-65535)
        众所周知端口: 1~1023(FTP: 21,SSH: 22, HTTP:80, HTTPS:469)
        保留端口: 1024-5000(不建议使用)
        可以使用的:5000~65535
        TCP端口和UDP端口是相互独立的
        网络里面的通信是由 IP地址+端口号 来决定

4.4 字节序

        字节序是指不同的CPU访问内存中的多字节数据时候,存在大小端问题

        如CPU访问的是字符串,则不存在大小端问题

一般来说:

   X86/ARM: 小端
  
powerpc/mips, ARM作为路由器时:大端模式

   网络传输的时候采用大端模式

IP地址转换函数:
        in_addr_t inet_addr(const char *cp);

        cp: 点分形式的IP地址,结果是32位整数(内部包含了字节序的转换,默认是网络字节序的模式)
         特点: 1. 仅适应于IPV4
                     2. 当出错时,返回-1
                     3.此函数不能用于255.255.255.255的转换

inet_pton()/inet_ntop()

        特点: 1.适应于IPV4和IPV6
                    2.能正确的处理255.255.255.255的转换问题
        参数:
                    1.af: 地址协议族(AF_INET或AF_INET6)
                    2.src:是一个指针(填写点分形式的IP地址[主要指IPV4])
                    3.dst: 转换的结果给到dst

RETURN VALUE
       inet_pton() returns 1 on success (network address was successfully con‐verted).  0 is returned if src  does  not contain  a  character  string representing a valid network address in the specified address family.  If af does not contain a valid address family, -1 is returned and errno is set to EAFNOSUPPORT.

五、TCP编程API 

1.socket()函数

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

1.1参数

        1.domain:
                AF_INET             IPv4 Internet protocols          ip(7)
                AF_INET6            IPv6 Internet protocols          ipv6(7)
                AF_UNIX, AF_LOCAL   Local communication              unix(7)
                AF_NETLINK          Kernel user interface device     netlink(7)
                AF_PACKET           Low level packet interface       packet(7)

        2.type:
                SOCK_STREAM: 流式套接字 唯一对应于TCP
                SOCK_DGRAM: 数据报套接字,唯一对应着UDP
                SOCK_RAW: 原始套接字
        3.protocol: 一般填0,原始套接字编程时需填充

1.2返回值:

RETURN VALUE
       On  success,  a  file  descriptor  for  the new socket is returned.  On
       error, -1 is returned, and errno is set appropriately.

成功时返回文件描述符,出错时返回为-1

2.bind()函数

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);

2.1 参数:
        sockfd: 通过socket()函数拿到的fd
        addr: struct sockaddr的结构体变量的地址 
        addrlen: 地址长度 

 RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.

        如果是IPV6的编程,要使用struct sockddr_in6结构体(详细情况请参考man 7 ipv6),通常更通用的方法可以通过struct sockaddr_storage来编程

3.listen()函数:

把主动套接字变成被动套接字

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int listen(int sockfd, int backlog);

参数:
        sockfd: 通过socket()函数拿到的fd

        backlog: 同时允许几路客户端和服务器进行正在连接的过程(正在三次握手)
一般填5, 测试得知,ARM最大为8

  内核中服务器的套接字fd会维护2个链表:
          1. 正在三次握手的的客户端链表(数量=2*backlog+1)
    2.已经建立好连接的客户端链表(已经完成3次握手分配好了newfd)

比如:listen(fd, 5); //表示系统允许11(=2*5+1)个客户端同时进行三次握手

返回值:

   RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.

 4.accept():

阻塞等待客户端连接请求

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

4.1参数:
                sockfd: 经过前面socket()创建并通过bind(),listen()设置过的fd
                addr和addrlen: 获取连接过来的客户的信息
4.2 返回值:
                RETURN VALUE
                On  success,  these system calls return a nonnegative integer that is a descriptor for the accepted socket.  On
                error, -1 is returned, and errno is set appropriately.

                成功时返回已经建立好连接的新的newfd

5.客户端的连接函数 connect()

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);

connect()函数和服务器的bind()函数写法类似:

5.1 参数:
        sockfd: 通过socket()函数拿到的fd
        addr: struct sockaddr的结构体变量的地址 
        addrlen: 地址长度 
5.2 返回值:
        RETURN VALUE
        If  the connection or binding succeeds, zero is returned.  On error, -1 is returned, and errno is set appropriately.

6. 套接字的关闭close()

•int close(int sockfd);
–关闭双向通讯

示例代码:

服务端:

#include "net.h"
#define flag 0
#if flag //多线程
    void *cli_data_handle(void *arg);
#else //多进程
    void cli_data_handle(void *arg);
#endif
void sig_child_handle(int signo)
{
    if(SIGCHLD == signo)
    {
        waitpid(-1,NULL,WNOHANG);
    }
}
int main(void)
{
    int fd;
    struct sockaddr_in sin;
    signal(SIGCHLD,sig_child_handle);
    //1.创建套接字
    if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
    {
        perror("socket");
        exit(1);
    }
    //优化4:允许绑定地址快速重用
    int b_reuse = 1;
    setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));

    memset(&sin,0,sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SERVER_PORT);
    //优化1:让服务器程序能绑定在任意的IP上
#if 0
    //sin.sin_addr.s_addr = inet_addr(SERVER_IP);
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
#else
    if(inet_pton(AF_INET,SERVER_IP,(void *)&sin.sin_addr) != 1)
    {
        perror("inet_pton");
        exit(1);
    }
#endif
    //2.绑定
    if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
    {
        perror("bind");
        exit(1);
    }
    //3.调用listen()把主动套接字变成被动套接字
    if(listen(fd,5) < 0)
    {
        perror("listen");
        exit(1);
    }
    printf("Server starting...OK!\n");
    //4.阻塞等待客户端连接请求
    int newfd;
    //printf("newfd = %d\n",newfd);
#if flag
    /*优化2:用多进程/多线程处理已经建立号连接的客户端数据*/
    pthread_t tid;
    while (1)
    {
        struct sockaddr_in cin;
        socklen_t addrlen = sizeof(cin);
        if((newfd = accept(fd,(struct sockaddr *)&cin,&addrlen)) < 0)
        {
            perror("accept");
            exit(1);
        }
        char ipv4_addr[16];
        //将ip地址的网络字节序转换为本地字节序
        if(!inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
        {
            perror("inet_ntop");
            exit(1);
        }
        //ntohs将端口号通过网络字节序转换为本地字节序
        printf("Client(%s:%d) is connected!\n",ipv4_addr,ntohs(cin.sin_port));
        pthread_create(&tid,NULL,cli_data_handle,(void *)&newfd);
    }
#else //多进程
    struct sockaddr_in cin;
    socklen_t addrlen = sizeof(cin);
    while (1)
    {
       pid_t pid = -1;
       if((newfd = accept(fd,(struct sockaddr *)&cin,&addrlen)) < 0)
        {
            perror("accept");
            break;
        }
        //创建一个子进程用于处理已建立连接的客户的交互数据
        if((pid = fork()) < 0)
        {
            perror("fork");
            break;
        }
        if (0 == pid) //子进程
        {
            close(fd);
            char ipv4_addr[16];
            //将ip地址的网络字节序转换为本地字节序
            if(!inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
            {
                perror("inet_ntop");
                exit(1);
            }
            //ntohs将端口号通过网络字节序转换为本地字节序
            printf("Client(%s:%d) is connected!\n",ipv4_addr,ntohs(cin.sin_port));
            cli_data_handle(&newfd);
            return 0;
        }
        else // 父进程
        {
            close(newfd);
        }
    }
    
#endif
    close(fd);
    return 0;
}
#if flag
    void  *cli_data_handle(void *arg)
#else
    void  cli_data_handle(void *arg)
#endif
{
    int newfd = *(int *)arg;
    #if flag //多线程
        printf("handler thread: newefd = %d\n",newfd);
    #else //多进程
        printf("Child handling process: newfd = %d\n",newfd);
    #endif
    
    int ret = -1;
    char buf[BUFSIZ];
    while (1)
    {
        memset(buf,0,sizeof(buf));
        do{
            ret = read(newfd,buf,BUFSIZ-1);
        }while(ret<0 && EINTR == errno);
        if (ret < 0)
        {
            perror("read");
            exit(1);
        }
        if(!ret)//对方已经关闭
        {
            break;
        }
        printf("Receive data: %s\n",buf);
        if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))//用户输入了quit字符
        {
            printf("Client(fd=%d) is exiting!\n",newfd);
            break;
        }
    }
    close(newfd);
}

客户端:

#include "net.h"

void usage(char *s)
{
    printf("\n%s serv_ip serv_port",s);
    printf("\n\t serv_ip: server ip address");
    printf("\n\t serv_port: server port(>5000)\n\n");
}
int main(int argc,char* argv[])
{
    int fd;
    int port;
    struct sockaddr_in sin;
    if (argc != 3)
    {
        usage(argv[0]);
        exit(1);
    }
    
    //1.创建套接字
    if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
    {
        perror("socket");
        exit(1);
    }
    port = atoi(argv[2]);
    if(port < 5000)
    {
        usage(argv[0]);
        exit(1);
    }
    //2.连接服务器
    memset(&sin,0,sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
#if 0
    sin.sin_addr.s_addr = inet_addr(SERVER_IP);
#else
    if(inet_pton(AF_INET,SERVER_IP,(void *)&sin.sin_addr) != 1)
    {
        perror("inet_pton");
        exit(1);
    }
#endif
    if(connect(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
    {
        perror("connect");
        exit(1);
    }
    printf ("Client staring...OK!\n");
    //3.读写数据
    char buf[BUFSIZ];
    int ret = -1;
    while (1)
    {
        memset(buf,0,sizeof(buf));
        if(fgets(buf,BUFSIZ-1,stdin) == NULL)
        {
            continue;
        }
        do
        {
            ret = write(fd,buf,strlen(buf));
        } while (ret < 0 && EINTR == errno);
        if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))//用户输入了quit字符
        {
            printf("Client is exiting!\n");
            break;
        }
    }
    //4.关闭套接字
    close(fd);
    //return 0;
}

运行结果:

重大知识点:(项目代码改进需要)

网络属性设置:


问题描述:在tcp连接下,如果服务器主动关闭连接(比如ctrl+c结束服务器进程),那么由于服务器这边会出现time_wait状态,所以不能立即重新启动服务器进程。

 通过netstat可以查看端口运行状态

-a (all)显示所有选项,默认不显示LISTEN相关
-t (tcp)仅显示tcp相关选项
-u (udp)仅显示udp相关选项
-n 拒绝显示别名,能显示数字的全部转化成数字。
-l 仅列出有在 Listen (监听) 的服務状态

-p 显示建立相关链接的程序名
-r 显示路由信息,路由表
-e 显示扩展信息,例如uid等
-s 按各个协议进行统计
-c 每隔一个固定时间,执行该netstat命令。

提示:LISTEN和LISTENING的状态只有用-a或者-l才能看到

服务端和客户端建立连接后,如果先断开服务端,再启动服务端的时候会出现如下bind错误:

服务端代码优化 :

创建套接字后,使用setsockopt函数对绑定地址快速重用就不会出现这个情况!!!! 

优化后重启服务器:

通过netstat命令查看这个端口上还是有time_wait的出现:

这时的time_wait是上次连接中与该地址和端口绑定的socket出现了time_wait状态,但是采用了端口复用后,可以多个socket和这个端口进行绑定,所以新的一次的连接使用的是新创建的socket和该端口进行绑定,并不会受到上一个socket处于time_wait的影响。

虽然可以立即启动服务器,但是对于高并发模式下的服务器在短时间内也是不能使用已经处于time_wait状态的socket的,要解决这样的问题就要用其它的方法(比如通过设置内核参数避免出现time_wait状态)。 上述中服务器能够立即重启是因为使用了新的socket和端口进行了绑定,而上次已经断开连接的socket还是在处于time_wait中的,这个socket在短时间内是不能再分配出去继续使用的。 

补充send()/recv()函数API 

网络接受数据:recv()/read() 

 

六、UDP编程API

发送数据sendto() :

接受数据recvfrom():

示例代码:

服务端:

#include "net.h"

int main(void)
{
    int fd;
    struct sockaddr_in sin;
    if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
    {
        perror("socket");
        exit(1);
    }
    //允许绑定地址快速重用(好像不加这个也可以)
    int b_reuse = 1;
    setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
    memset(&sin,0,sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SERVER_PORT);
    sin.sin_addr.s_addr = inet_addr(SERVER_IP);
    if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
    {
        perror("bind");
        exit(1);
    }
    char buf[BUFSIZ];
    struct sockaddr_in cin;
    socklen_t addrlen = sizeof(cin);
    while (1)
    {

        memset(buf,0,sizeof(buf));
        if(recvfrom(fd,buf,BUFSIZ-1,0,(struct sockaddr *)&cin,&addrlen) < 0)
        {
            perror("recvfrom");
            exit(1);
        }
        char ipv4_addr[16];
        if (!inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
        {
            perror("inet_ntop");
            exit(1);
        }
        printf("Recvied from(%s:%d),data:%s\n",ipv4_addr,ntohs(cin.sin_port),buf);
    }

    close(fd);
    return 0;
}

客户端:

#include "net.h"

int main(void)
{
    int fd;
    struct sockaddr_in sin;

    if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
    {
        perror("socket");
        exit(1);
    }
    memset(&sin,0,sizeof(sin));

    sin.sin_family = AF_INET;
    sin.sin_port = htons(SERVER_PORT);
    sin.sin_addr.s_addr = inet_addr(SERVER_IP);
    char buf[BUFSIZ];
    while (1)
    {
        memset(buf,0,sizeof(buf));
        if(fgets(buf,BUFSIZ-1,stdin) == NULL)
        {
            perror("fgets");
            continue;
        }
        if(sendto(fd,buf,strlen(buf),0,(struct sockaddr *)&sin,sizeof(sin)) < 0)
        {
            perror("sendto");
            exit(1);
        }
    }
    
    
    close(fd);
    return 0;

}

头文件:

#ifndef __NET_H_
#define __NET_H_
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>

#define SERVER_PORT 6780
#define SERVER_IP "192.168.1.66"

#endif 

运行结果:

udp可以实现多个客户端对一个服务器 

七、TCP、IP协议原理

TCP是一种面向连接,可靠的数据传输 

TCP的可靠传输:通过确认和重发机制

  • TCP把所有要发送的数据进行编号(每一个字节用一个号)
  • 发送时从当前数据位置,发送window大小的数据

三次握手、四次挥手 

服务端启动./client        客户端启动./server

整个流程: 

wireshark工作原理: 

使用wireshark软件进行抓包:

可以看到三次握手由客户端发起,完全符合下图流程

第一次握手传输层报文:SYN = 1,Seq = 0

第二次握手传输层报文:SYN = 1,ACK = 1,Seq = 0,ack = 1

第三次握手传输层报文:ACK = 1,seq = 1,ack = 1

各字段在TCP三次握手中的作用:

SYN:用于建立连接。

ACK:用于确定收到了请求。

seq:发送自己的数据。

ack:发送接收到的对方的数据。


上图为四次挥手:

第一次挥手传输报文:FIN = 1,ACK = 1,seq = 12,ack = 14(怀疑老师讲错了,他的意思是FIN = m = 12)

第二次挥手传输报文:FIN = 1,ACK = 1,seq = 14,ack = 13   (怀疑老师讲错了,他的意思是ACK = m+1 = 13,FIN = n=14)(其中通过wireshark看到这里包含了两次挥手)

第三次挥手传输报文:ACK = 1,seq = 13,ack = 15(怀疑老师讲错了,他的意思是ACK = n+1 = 15)

注意事项:

三次握手的连接 必须由客户端发起(四次挥手客户端和服务器都可以发起)

重点学习文章:活久见!TCP两次挥手,你见过吗?那四次握手呢?-CSDN博客

八、unix域套接字(unix domain)

用于本地进程间的通信

        创建套接字时使用本地协议PF_UNIX(或PF_LOCAL)

                socket(AF_LOCAL,SOCK_STREAM,0)

                socket(AF_LOCAL,SOCK_DGRAM,0)

        分为流式套接字和用户数据报套接字

进程间通信:

1、进程间的数据共享:

        管道、消息队列、共享内存、unix域套接字

易用性:消息队列 > unix域套接字 > 管道 > 共享内存(经常要和信号量一起用)

效率:共享内存 > unix域套接字 > 管道 > 消息队列

常用:共享内存、unix域套接字

2、异步通信:

        信号

3、同步和互斥(做资源保护)

        信号量

示例代码

服务端:

#include "net.h"

int main(void)
{
    int fd;
    struct sockaddr_un sun;

    if((fd = socket(AF_UNIX,SOCK_STREAM,0)) < 0)
    {
        perror("socket");
        exit(1);
    }
    //允许绑定地址快速重用
    int b_reuse = 1;
    setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));

    memset(&sun,0,sizeof(sun));
    sun.sun_family = AF_UNIX;
    //如果unix所指向的文件存在,则删除
    if(!access(UNIX_DOMAIN_FILE,F_OK))
    {
        unlink(UNIX_DOMAIN_FILE);
    }
    strncpy(sun.sun_path,UNIX_DOMAIN_FILE,strlen(UNIX_DOMAIN_FILE));

    if(bind(fd,(struct sockaddr *)&sun,sizeof(sun)) < 0)
    {
        perror("bind");
        exit(1);
    }
    if(listen(fd,5) < 0)
    {
        perror("listen");
        exit(1);
    }
    printf("server starting ...... OK\n");

    int newfd;
    if((newfd = accept(fd,NULL,NULL)) < 0)
    {
        perror("accept");
        exit(1);
    }
    int ret = -1;
    char buf[BUFSIZ];
    while (1)
    {
        memset(buf,0,sizeof(buf));
        // read(newfd,buf,BUFSIZ-1);
        // printf("receive data: %s\n",buf);
        do{
            ret = read(newfd,buf,BUFSIZ-1);
        }while(ret<0 && EINTR == errno);
        if (ret < 0)
        {
            perror("read");
            exit(1);
        }
        if(!ret)//对方已经关闭
        {
            break;
        }
        printf("Receive data: %s\n",buf);
    }
    close(newfd);
    close(fd);
    return 0;

}

客户端:

#include "net.h"

int main(void)
{
    int fd;
    struct sockaddr_un sun;
    
    
    if((fd = socket(AF_UNIX,SOCK_STREAM,0)) < 0)
    {
        perror("socket");
        exit(1);
    }
    memset(&sun,0,sizeof(sun));
    sun.sun_family = AF_UNIX;
    //确保UNIX_DOMAIN_FILE存在可写
    if((access(UNIX_DOMAIN_FILE,F_OK|W_OK)) < 0)
    {
        exit(1);
    }
    strncpy(sun.sun_path,UNIX_DOMAIN_FILE,strlen(UNIX_DOMAIN_FILE));
    if(connect(fd,(struct sockaddr *)&sun,sizeof(sun)) < 0)
    {
        perror("connect");
        exit(1);
    }
    printf("client starting ok\n");

    char buf[BUFSIZ];
    while (1)
    {
        memset(buf,0,sizeof(buf));
        if(fgets(buf,BUFSIZ-1,stdin) == NULL)
        {
            continue;
        }
        write(fd,buf,strlen(buf));
    }
    return 0;
}

net.h文件

#ifndef __NET_H_
#define __NET_H_

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>   
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define UNIX_DOMAIN_FILE "./my_domain_file.txt"
#endif

运行结果:

相关推荐

  1. tcp网络编程基础

    2024-03-25 14:48:04       32 阅读
  2. 基于UDP网络编程

    2024-03-25 14:48:04       54 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-25 14:48:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-25 14:48:04       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-25 14:48:04       82 阅读
  4. Python语言-面向对象

    2024-03-25 14:48:04       91 阅读

热门阅读

  1. 位运算符与位移运算符

    2024-03-25 14:48:04       34 阅读
  2. SGP.22-v2.2.2-Application-note

    2024-03-25 14:48:04       43 阅读
  3. NIO详解

    2024-03-25 14:48:04       38 阅读
  4. Switch搜不到5g wifi

    2024-03-25 14:48:04       37 阅读
  5. css使用伪类选择器来选择特定模式的元素

    2024-03-25 14:48:04       40 阅读
  6. RabbitAdmin及 RabbitMQ 事件处理

    2024-03-25 14:48:04       41 阅读
  7. Ubuntu Desktop Server - user 用户与 root 用户切换

    2024-03-25 14:48:04       40 阅读
  8. 探秘ChatGPT:打造出色学术论文

    2024-03-25 14:48:04       38 阅读