服务器模型 setsockopt 网络超时检测 广播组播和unix域套接字 5.23

news2024/11/24 10:18:06

四.服务器模型

1.循环服务器

TCP服务器
TCP服务器端运行后等待客户端的连接请求。
TCP服务器接受一个客户端的连接后开始处理,完成了客户的所有请求后断开连接。 
TCP循环服务器一次只能处理一个客户端的请求。
只有在当前客户的所有请求都完成后,服务器才能处理下一个客户的连接/服务请求。
缺点:
    如果某个客户端一直占用服务器资源,那么其它的客户端都不能被处理。TCP服务器一般很少采用循环服务器模型。 

流程如下:
	socket(...); 
	bind(...); 
	listen(...); 
	while(1) 
    { 
        connfd = accept(...); 
        while(1) 
        { 
            recv(...); 
            process(...); 
            send(...); 
        } 
      	close(connfd); 
    }

2.并发服务器

为了弥补TCP循环服务器的缺陷,人们又设计了并发服务器的模型。
并发服务器的设计思想是服务器接受客户端的连接请求后创建子进程来为客户端服务 
TCP并发服务器可以避免TCP循环服务器中客户端独占服务器的情况。
为了响应客户机的请求,服务器要创建子进程来处理。 如果有多个客户端的话,服务器端需要创建多个子进程。过多的子进程会影响服务器端的运行效率

流程如下:
1. 多线程:
	void *accept_client(void *arg)
    {
        //int connfd = *((int*)arg);
        int connfd = (int)arg;
        while(1)   //和客户端通信部分
        { 
            recv(...); 
            process(...); 
            send(...); 
        }
        close(connfd);
        pthread_exit(NULL);
    }

	sockfd = socket(...); 
	bind(...); 
	listen(...); 
	while(1) 
    { 
        int connfd = accept(...); 
        //pthread_create(&thread, NULL, accept_client, (void*)&connfd);
        pthread_create(&thread, NULL, accept_client, (void*)connfd);
        pthread_detach(thread);
    }
	close(sockfd);

2. 多进程:
    void handler(int sig)
	{
    	while (waitpid(-1, NULL, WNOHANG)>0);
	}
    
    sockfd = socket();
	bind();
	listen();
	signal(SIGCHLD, handler);

	while (1)
    {
        connfd = accept();
        pid = fork();
        if (0 == pid)  //子进程
        {
            close(sockfd);
            while (1)  
            {
                //与客户端通信
            }
            close(connfd);
            exit(0);
        }
        close(connfd); 
    }
	close(sockfd);



/
多进程服务器
  /*===============================================
 *   文件名称:server.c
 *   创 建 者:memories 
 *   创建日期:2023年05月18日
 *   描    述:
 ================================================*/
    
    
这里重点提一下SIGCHLD信号(17号信号),对于父进程而言,一般要对它的子进程进行等待,以防止子进程变为僵尸进程,而导致内存泄漏问题,但是对于父进程的等待方式存在两种:阻塞式等待和非阻塞式等待,可以通过wait和waitpid这两个系统调用来实现,对于父进程而言,阻塞式等待让父进程停下自己手头上的事情,专心的等待子进程退出,这严重影响了父进程的工作;而对于非阻塞式等待,父进程每隔一段时间都要轮循一下,看看子进程有没有退出,这也让我们的程序变的复杂,所以我们就想能不能让父进程对于子进程的等待变成子进程退出了就通知父进程进行回收,也就是让对子进程的等待变成异步等待,不干扰父进程的正常工作呢?

这里就得了解一下父进程是如何知道子进程已经退出的,也就是我们的SIGCHLD这个信号的作用,当子进程退出时,会向父进程发送SIGCHLD信号,而父进程就是根据是否收到这个信号来判断子进程是否退出的



while(waitpid(-1,NULL,WNOHANG)>0);

一次结束可以回收多个尸体

(假如有五个客户端同时停止,waitpid(-1,NULL,WNOHANG)一次只会回收一个尸体,因为SIGCHLD只会执行一次,在第二次回收第二剧尸体时发现SIGCHLD已经被调用,就不会在第二次调用。。在这里采用while循环重复调用,waitpid的返回值若>0则资源还没有回收完,=0子进程资源已经回收完)    
    

#include <stdio.h>
#include "ip.h"


void handler(int arg)
{
    while(waitpid(-1,NULL,WNOHANG)>0);
}

int main(int argc, char *argv[])
{ 
    //1.创建套接字
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
    {
        perror("socket");
        return -1;
    }
    printf("socket-----------------\n");
    //2.绑定本机地址和端口
    //用IPv4结构体
    struct sockaddr_in srvaddr;
    memset(&srvaddr,0,sizeof(srvaddr));
    srvaddr.sin_family = AF_INET;//指定地址族为IPv4的地址
    srvaddr.sin_port = htons(54320);//端口号
    srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);//将点分式的字符串转化32位的网络字节序
    //设置重用本地地址和端口
    int on = 1;
    if(0 > setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))
    {
        perror("setsockopt");
        return -1;
    }
    if(0 > bind(sockfd,(struct sockaddr*)&srvaddr,sizeof(srvaddr)))
    {
        perror("bind");
        return -1;
    }
    printf("bind-----------------\n");
    //3.设置监听套接字
    if(0>listen(sockfd,5))//必须写大于1的整数
    {
        perror("listen");
        return -1;
    }
    printf("listen-----------------\n");
    signal(SIGCHLD,handler);//信号灯集,异步通信

    while(1)
    {
        //4.接受客户端的连接,并生成通信套接字
        int connfd = accept(sockfd,NULL,NULL);
        if(connfd < 0)
        {
            perror("accept");
            return -1;
        }
        printf("accept-----------------\n");
        pid_t pid=fork();
        if(pid<0)
        {
            perror("fork");
            return -1;
        }

        else if(pid == 0)
        {
            close(sockfd);
            //设置超时
            struct timeval tv={1};
            if(0 > setsockopt(connfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)))
            {
                perror("setsockopt");
                return -1;
            }
            //5.与客户端通信
            int ret;
            char buf[1024];
            while(1)
            {
                memset(buf,0,sizeof(buf));
                ret = read(connfd,buf,sizeof(buf));
                if(ret < 0)
                {
                    perror("read");
                    continue;
                }
                else if(ret == 0)
                {
                    printf("write close\n");
                    break;
                }
                printf("recv:%s\n",buf);

                if(0 > write(connfd,buf,ret))
                {
                    perror("write");
                    return -1;
                }
            }
        }
        else
            close(connfd);

    }
    return 0;
}

//
多线程
    
/*===============================================
 *   文件名称:server.c
 *   创 建 者:memories 
 *   创建日期:2023年05月18日
 *   描    述:
 ================================================*/
#include <stdio.h>
#include "ip.h"
int *process1(void *arg);
void handler(int arg);

int main(int argc, char *argv[])
{ 
    //1.创建套接字
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
    {
        perror("socket");
        return -1;
    }
    printf("socket-----------------\n");
    //2.绑定本机地址和端口
    //用IPv4结构体
    struct sockaddr_in srvaddr;
    memset(&srvaddr,0,sizeof(srvaddr));
    srvaddr.sin_family = AF_INET;//指定地址族为IPv4的地址
    srvaddr.sin_port = htons(5420);//端口号
    srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);//将点分式的字符串转化32位的网络字节序
    int on = 1;
    if(0 > setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))
    {
        perror("setsockopt");
        return -1;
    }
    if(0 > bind(sockfd,(struct sockaddr*)&srvaddr,sizeof(srvaddr)))
    {
        perror("bind");
        return -1;
    }
    printf("bind-----------------\n");
    //3.设置监听套接字
    if(0>listen(sockfd,5))//必须写大于1的整数
    {
        perror("listen");
        return -1;
    }
    printf("listen-----------------\n");
    /
    SAI cliaddr;
    socklen_t addrlen = sizeof(cliaddr);
    while(1)
    {
        //4.接受客户端的连接,并生成通信套接字
        int connfd = accept(sockfd,(SA *)&cliaddr,&addrlen);
        if(connfd < 0)
        {
            perror("accept");
            return -1;
        }
        printf("accept success:%s %hu\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));

        pthread_t tid,tid2;
        if(0 != pthread_create(&tid,NULL,(void *)process1,(void *)&connfd))
        {
            printf("pthread creat failed\n");
            return -1;
        }
        //线程分离
        if(0 != pthread_detach(tid))
            break;
        /*
           if(0 > pthread_create(&tid2,NULL,process2,(void *)&connfd))
           {
           printf("pthread2 creat failed\n");
           return -1;
           }
           */

    }
    //6.关闭套接字
    close(sockfd);
    return 0;
} 

void handler(int arg)
{
    printf("timeout\n");
}

int *process1(void *arg)
{
    int connfd = *((int *)arg);
       struct timeval tv={1};
       if(0 > setsockopt(connfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)))
       {
       perror("setsockopt");
       return NULL;
       }
       
    /*
    struct sigaction act;
    sigaction(SIGALRM,NULL,&act);
    act.sa_handler = handler;
    act.sa_flags &= ~SA_RESTART; 
    sigaction(SIGALRM,&act,NULL);
    */
    //5.与客户端通信
    int ret;
    char buf[1024];
    while(1)
    {
      //  alarm(1);
        memset(buf,0,sizeof(buf));
        ret = read(connfd,buf,sizeof(buf));
        if(ret < 0)
        {
            perror("read");
            continue;
        }
        else if(ret == 0)
        {
            printf("write close\n");
            break;
        }
        printf("recv:%s\n",buf);

        if(0 > write(connfd,buf,ret))
        {
            perror("write");
            return NULL;
        }
    }
    close(connfd);
    pthread_exit(NULL);

}

五.进阶篇

应用层知道底层的信息:网络信息检索函数

1.setsockopt()

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

       int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
       int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

作用:获取/设置套接字属性
参数:
    sockfd --- 套接字文件描述符
    level --- 选项所在层
    			1)SOL_SOCKET:通用套接字选项
                2)IPPROTO_IP:IP选项
                3)IPPROTO_TCP:TCP选项
    optname --- 选项的名字
    optval --- 选项的值
    optlen --- 选项的长度
    
返回值:
     成功:0
     失败:-1,并设置error
                    
/*设置重用本地地址和端口*/
int on = 1;
if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
{
    perror("setsockopt");
    return -1;
}
/*===============================================
*   文件名称:server.c
*   创 建 者:     
*   创建日期:2023年05月18日
*   描    述:
================================================*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{ 
    /*1. 创建套接字*/
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket");
        return -1;
    }
    printf("socket..............\n");

    /*设置重用本地地址和端口*/
    int on = 1;
    if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
    {
        perror("setsockopt");
        return -1;
    }

    /*2. 绑定本机地址和端口*/
    struct sockaddr_in srvaddr;
	memset(&srvaddr, 0, sizeof(srvaddr));
	srvaddr.sin_family 	= AF_INET;  //地址族
	srvaddr.sin_port 	= htons(6666); //端口号
    srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);  //IP地址

	if (0 > bind(sockfd, (struct sockaddr*)&srvaddr, sizeof(srvaddr))) 
    {
        perror("bind");
        return -1;
    }
    printf("bind................\n");

    /*3. 设置监听套接字*/
    if (0 > listen(sockfd, 5))
    {
        perror("listen");
        return -1;
    }
    printf("listen.................\n");

    /*4. 接受客户端的连接,并生成通信套接字*/
    int connfd = accept(sockfd, NULL, NULL);
    if (connfd < 0)
    {
        perror("accept");
        return -1;
    }
    printf("accept success!\n");

    /*接收超时*/
    struct timeval tv = {1};
    if (0 > setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
    {
        perror("setsockopt");
        return -1;
    }

    /*5. 与客户端通信*/
    int ret;
    char buf[1024];
    while (1)
    {
        memset(buf, 0, sizeof(buf));
        ret = read(connfd, buf, sizeof(buf));
        if (ret < 0)
        {
            perror("read");
            continue;
        }
        else if (0 == ret)
        {
            printf("write close!\n");
            break;
        }
        printf("recv: %s\n", buf);
/*
        if (0 > write(connfd, buf, ret))
        {
            perror("write");
            break;
        }
*/
    }

    /*6. 关闭套接字*/
    close(connfd);
    close(sockfd);

    return 0;
} 

2.网络超时检测

网络超时

在网络通信中,很多操作会使得进程阻塞

TCP套接字中的recv/ accept/ connect

UDP套接字中的recvfrom

超时检测的必要性
避免进程在没有数据时无限制地阻塞(例如有两个客户端连接同一个服务器,当客户端1没有输入数据时,服务器就会阻塞等待1的输入,大大浪费了服务器的资源,所以采用网络超时检测,循环检测,当客户端2输入时也能检测出运行到)
当设定的时间到时,进程从原操作返回继续运行

1)setsockopt

/*接收超时*/
struct timeval tv = {1};
if (0 > setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
{
    perror("setsockopt");
    return -1;
}


 ret = read(connfd,buf,sizeof(buf));
                if(ret < 0)
                {
                    perror("read");
                    continue;
                }
                else if(ret == 0)
                {
                    printf("write close\n");
                    break;
                }
                printf("recv:%s\n",buf);

2)select/poll

struct timeval va ={1,1000000};
        //2.监测是否有文件描述符产生了事件
        int  retval = select(maxfd+1,&rfds,NULL,NULL,&va);
        if(retval < 0)
        {
            perror("select");
            break;
        }
        else if(0 == retval)
        {
            printf("timeout------\n");
            continue;
        }
若没数据输入则每隔2s打印timeout------

3)sigaction()

设置定时器(timer), 捕捉SIGALRM信号
参考代码如下
    void  handler(int signo)     {   return;  }
//一旦进程收到这个信号,执行完信号处理函数之后,下一个函数就会直接返回,不阻塞在那里
    struct sigaction act;
	sigaction(SIGALRM, NULL, &act);  //获取信号原有的act
	act.sa_handler = handler;
	act.sa_flags &= ~SA_RESTART;   //把原有的act改成需要的act
	sigaction(SIGALRM, &act, NULL); //设置信号新的act
	
    /*5. 与客户端通信*/
    int ret;
    char buf[1024];
    while (1)
    {
		alarm(1);  //闹钟函数,产生SIGALRM信号
        recv()
    }
|= :添加某功能

&= ~:去除某功能

3.广播

在这里插入图片描述

前面介绍的数据包发送方式只有一个接受方,称为单播

如果同时发给局域网中的所有主机,称为广播

只有用户数据报(使用UDP协议)套接字才能广播

广播地址

​ 以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址

​ 发到该地址的数据包被所有的主机接收

​ 255.255.255.255在所有网段中都代表广播地址

1. 发送方
    创建用户数据报套接字

    缺省创建的套接字不允许广播数据包,需要设置属性
    setsockopt可以设置套接字属性

	接收方地址指定为广播地址以及端口信息

	发送数据包
    
2. 接收方
    创建用户数据报套接字
    
    绑定IP地址(广播IP或0.0.0.0)和端口
	绑定的端口必须和发送方指定的端口相同

	等待接收数据
	
	
	
	
	
#include <stdio.h>
#include "net.h"
/
    接收方
int main()
{
	/*1. 创建用户数据报套接字*/
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		perror("socket");
		return -1;
	}
	
	/*允许重用本地地址和端口*/
	int on = 1;
	if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
	{
		perror("setsockopt");
		return -1;
	}
	
	/*2. 绑定广播地址和端口*/
	SAI bcaddr = {
		.sin_family = AF_INET,
		.sin_port = htons(6666),
		.sin_addr.s_addr = inet_addr("192.168.2.255") //广播IP
	};
	if (0 > bind(sockfd, (SA*)&bcaddr, sizeof(bcaddr)))
	{
		perror("bind");
		return -1;
	}
	
	/*3. 接收数据*/
	SAI sndaddr;
	socklen_t addrlen = sizeof(sndaddr);
	int ret;
	char buf[SIZE];
	while (1)
	{
		ret = recvfrom(sockfd, buf, SIZE, 0, (SA*)&sndaddr, &addrlen);
		if (ret < 0)
		{
			perror("recvfrom");
			break;
		}
		printf("recv: %s\n", buf);
	}
	
	return 0;
}




/
发送方

#include <stdio.h>
#include "net.h"

int main()
{
	/*1. 创建用户数据报套接字*/
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		perror("socket");
		return -1;
	}

    /*2. 设置允许发送广播包*/
	int on = 1;
	if (0 > setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)))
	{
		perror("setsockopt");
		return -1;
	}

	/*3. 接收方地址指定为广播地址以及端口信息*/
	SAI rcvaddr = {
		.sin_family = AF_INET,
		.sin_port = htons(6666),
		.sin_addr.s_addr = inet_addr("192.168.2.255") //广播IP
	};

	/*4. 发送数据包*/
	char buf[SIZE];
	while (1)
	{
		printf("send: ");
		fgets(buf, SIZE, stdin);
		if (0 > sendto(sockfd, buf, SIZE, 0, (SA*)&rcvaddr, sizeof(rcvaddr)))
		{
			perror("sendto");
			break;
		}
	}

	return 0;
}


4.组播

单播方式只能发给一个接收方。

广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。

组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。

多播方式既可以发给多个主机,又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)
    
组播地址:
    224.0.0.1239.255.255.255

1. 发送方
    创建用户数据报套接字

	接收方地址指定为组播地址以及端口信息

	发送数据包
    
2. 接收方
    a. 创建用户数据报套接字
    
    b. 加入多播组
		struct ip_mreq{
             struct  in_addr  imr_multiaddr; 
             struct  in_addr  imr_interface;
        };
        struct  ip_mreq  mreq;

        bzero(&mreq, sizeof(mreq));
        mreq.imr_multiaddr.s_addr = inet_addr(224.10.10.1); //组播IP
        mreq.imr_interface.s_addr = htonl(INADDR_ANY);  //本机IP
        setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,  sizeof(mreq));
    
    c. 绑定IP地址(组播IP或0.0.0.0)和端口
		绑定的端口必须和发送方指定的端口相同

	d. 等待接收数据
        
        
        
        
        

        接收数据
        #include <stdio.h>
#include "net.h"

int main()
{
	/*1. 创建用户数据报套接字*/
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		perror("socket");
		return -1;
	}
	
	/*2. 加入多播组*/
	struct ip_mreq{
		 struct  in_addr  imr_multiaddr; 
		 struct  in_addr  imr_interface;
	};
	struct  ip_mreq  mreq;

	bzero(&mreq, sizeof(mreq));
	mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.1"); //组播IP
	mreq.imr_interface.s_addr = htonl(INADDR_ANY);  //本机IP
	if (0 > setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,  sizeof(mreq)))
	{
		perror("setsockopt");
		return -1;
	}
	
	/*允许重用本地地址和端口*/
	int on = 1;
	if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
	{
		perror("setsockopt");
		return -1;
	}
	
	/*3. 绑定组播地址和端口*/
	SAI mcaddr = {
		.sin_family = AF_INET,
		.sin_port = htons(6666),
		.sin_addr.s_addr = inet_addr("224.0.0.1") //组播IP
	};
	if (0 > bind(sockfd, (SA*)&mcaddr, sizeof(mcaddr)))
	{
		perror("bind");
		return -1;
	}
	
	/*4. 接收数据*/
	SAI sndaddr;
	socklen_t addrlen = sizeof(sndaddr);
	int ret;
	char buf[SIZE];
	while (1)
	{
		ret = recvfrom(sockfd, buf, SIZE, 0, (SA*)&sndaddr, &addrlen);
		if (ret < 0)
		{
			perror("recvfrom");
			break;
		}
		printf("recv: %s, %hu, %s\n", inet_ntoa(sndaddr.sin_addr), \
			ntohs(sndaddr.sin_port), buf);
	}
	
	return 0;
}


/
发送数据
    #include <stdio.h>
#include "net.h"

int main()
{
	/*1. 创建用户数据报套接字*/
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		perror("socket");
		return -1;
	}

	/*2. 接收方地址指定为组播地址以及端口信息*/
	SAI rcvaddr = {
		.sin_family = AF_INET,
		.sin_port = htons(6666),
		.sin_addr.s_addr = inet_addr("224.0.0.1") //组播IP
	};

	/*3. 发送数据包*/
	char buf[SIZE];
	while (1)
	{
		printf("send: ");
		fgets(buf, SIZE, stdin);
		if (0 > sendto(sockfd, buf, SIZE, 0, (SA*)&rcvaddr, sizeof(rcvaddr)))
		{
			perror("sendto");
			break;
		}
	}

	return 0;
}


    

5.unix域套接字

socket同样可以用于本地通信

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

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

和其他进程间通信方式相比使用方便、效率更高

常用于前后台进程通信

本地地址结构
       struct sockaddr_un        //  <sys/un.h>
        {
             sa_family_t  sun_family;
             char  sun_path[108];         // 套接字文件的路径
         };
填充地址结构
    struct sockaddr_un myaddr;
     bzero(&myaddr,  sizeof(myaddr));
     myaddr.sun_family = PF_UNIX; 
     strcpy(myaddr.sun_path,  “mysocket”);

流式:
    server:
    	socket(AF_UNIX, SOCK_STREAM, 0);
		bind(本地地址);
		listen();
		accept();
        recv/send();
		close();
    client:
    	socket(AF_UNIX, SOCK_STREAM, 0);
		connect(本地地址);
		send/recv();
		close();
数据报:
	server:
		socket(AF_UNIX, SOCK_DGRAM, 0);
		bind(本地地址);
		sendto/recvfrom();
		close();
	client:
		socket(AF_UNIX, SOCK_DGRAM, 0);
		recvfrom()/sendto();
		close();
/*===============================================
*   文件名称:client.c
*   创 建 者:     
*   创建日期:2023年05月19日
*   描    述:
================================================*/

发送端
    
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/un.h>

int main(int argc, char *argv[])
{ 
    /*1. 创建套接字*/
    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket");
        return -1;
    }
    printf("socket...........\n");

    /*2. 主动连接服务器*/
    struct sockaddr_un srvaddr;
	memset(&srvaddr, 0, sizeof(srvaddr));
	srvaddr.sun_family 	= AF_UNIX;  //地址族
    strcpy(srvaddr.sun_path, "mysocket");

	if (0 > connect(sockfd, (struct sockaddr*)&srvaddr, sizeof(srvaddr))) 
    {
        perror("connect");
        return -1;
    }
    printf("connect................\n");

    /*3. 与服务器通信*/
    int ret;
    char buf[1024];
    while (1)
    {
        printf("Send: ");
        fgets(buf, sizeof(buf), stdin);
        if (0 > write(sockfd, buf, sizeof(buf)))
        {
            perror("write");
            break;
        }
    }

    /*4. 关闭套接字*/
    close(sockfd);

    return 0;
} 




*===============================================
*   文件名称:server.c
*   创 建 者:     
*   创建日期:20230518*   描    述:
================================================*/
    
    接收方    
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/un.h>

int main(int argc, char *argv[])
{ 
    /*1. 创建套接字*/
    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket");
        return -1;
    }
    printf("socket..............\n");

    /*2. 绑定本地地址*/
    struct sockaddr_un srvaddr;
	memset(&srvaddr, 0, sizeof(srvaddr));
	srvaddr.sun_family 	= AF_UNIX;  //地址族
    strcpy(srvaddr.sun_path, "mysocket");

	if (0 > bind(sockfd, (struct sockaddr*)&srvaddr, sizeof(srvaddr))) 
    {
        perror("bind");
        return -1;
    }
    printf("bind................\n");

    /*3. 设置监听套接字*/
    if (0 > listen(sockfd, 5))
    {
        perror("listen");
        return -1;
    }
    printf("listen.................\n");

    /*4. 接受客户端的连接,并生成通信套接字*/
    int connfd = accept(sockfd, NULL, NULL);
    if (connfd < 0)
    {
        perror("accept");
        return -1;
    }
    printf("accept success!\n");

    /*5. 与客户端通信*/
    int ret;
    char buf[1024];
    while (1)
    {
        memset(buf, 0, sizeof(buf));
        ret = read(connfd, buf, sizeof(buf));
        if (ret < 0)
        {
            perror("read");
            break;
        }
        else if (0 == ret)
        {
            printf("write close!\n");
            break;
        }
        printf("recv: %s\n", buf);
/*
        if (0 > write(connfd, buf, ret))
        {
            perror("write");
            break;
        }
*/
    }

    /*6. 关闭套接字*/
    close(connfd);
    close(sockfd);

    return 0;
} 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/560564.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Lucene(6):分词器

1 分词理解 在对Document中的内容进行索引之前&#xff0c;需要使用分词器进行分词 &#xff0c;分词的目的是为了搜索。分词的主要过程就是先分词后过滤。 分词&#xff1a;采集到的数据会存储到document对象的Field域中&#xff0c;分词就是将Document中Field的value值切分…

netty学习第一课

技术主题 Netty是一个基于Java NIO&#xff08;非阻塞 I/O&#xff09;框架的网络编程框架。它提供了一系列的高级网络编程API&#xff0c;使得开发者可以非常容易地实现高性能、高可靠性的网络应用。Netty具有非常好的可扩展性和灵活性&#xff0c;能够很好地支持多种协议和数…

Fiddler抓包工具之fiddler的介绍及安装

Fiddler简介 Fiddler是比较好用的web代理调试工具之一&#xff0c;它能记录并检查所有客户端与服务端的HTTP/HTTPS请求&#xff0c;能够设置断点&#xff0c;篡改及伪造Request/Response的数据&#xff0c;修改hosts&#xff0c;限制网速&#xff0c;http请求性能统计&#xff…

从零实现一个数据库(DataBase) Go语言实现版 7.空闲列表: 重用页

英文源地址 由于我们的B树时不可变的, 每次对kv存储的更新都会在路径上创建新节点, 而不是更新当前节点, 从而使一些节点无法从最新版本访问到.我们需要从旧版本中重用这些不可访问的节点, 否则, 数据库文件将无限增长. 设计空闲列表 为了重用这些页, 我们将添加一个持久化存…

python处理字符串、文本实例及注释

1、多个界定符切割字符串 代码 line = asdf fjdk; afed, fjek,asdf, foo import re re.split(r[;,\s]\s*, line) 结果 在上面的例子中,分隔符可以是逗号,分号或者是空格,并且后面紧跟着任意个的空格。只要这个模式被找到,那么匹配的分隔符两边的实体都会被当成是结果中…

面了个20k的自动化测试,从腾讯出来的果然都有两把刷子···

现在找个会自动化测试的人真是难呀&#xff0c;10个里面有8个写了会自动化&#xff0c;但一问就是三不知 公司前段时间缺人&#xff0c;也面了不少测试&#xff0c;前面一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在15-20k&#xff0c;面试的…

技巧:如何查看github的热门趋势和star排行

目录 1. 查看github的热门趋势2. 查看github的star排行3. 如何查看项目star增长曲线 1. 查看github的热门趋势 手动找到入口&#xff0c;打开github&#xff0c;登录后&#xff0c;找到Explore并点击进入&#xff0c;找到Trending切换&#xff0c;列出的就是github当天所有语言…

目标检测常用模型之R-CNN、Fast R-CNN、Faster R-CNN

文章目录 一、模型分类1. 一阶段目标检测2. 二阶段目标检测 二、常见模型1. R-CNN2. Fast R-CNN3. Faster R-CNN 一、模型分类 2012年卷积神经网络(Convolutional Neural Networks, CNNs)的兴起将目标检测领域推向了新的台阶。基于CNNs的目标检测算法主要有两条技术发展路线&am…

国外顶尖高校、企业分享人工智能自学课程英文原课程分享

人工智能无疑已经是当下最火热的方向&#xff0c;在很多领域已经融入我们生活&#xff0c;ChatGPT,Midjourney只是其中一个细分热点。目前这个领域&#xff0c;虽说国内也有不少课程&#xff0c;但是大部分源头还得从英文资料中找。如何学到最新最强得人工智能技能&#xff0c;…

Mongodb——快速入门,2个小时足够了

目录 1、Mongodb概述 1.1、为何使用Mongodb&#xff1f; 1.2、业务应用场景 1.3、Mongodb和MySQL的区别 2、Mongodb安装 2.1、Windows系统中安装启动 3、Mongodb的操作 3.1、数据库操作 3.2、集合操作 3.2.1、集合显式创建 3.2.2、集合的隐式创建 3.2.3集合的删除 …

k8s进阶3——资源配额、资源限制

文章目录 一、基本了解1.1 资源计算1.2 调度机制1.3 服务质量等级 二、资源配额 ResourceQuota2.1 支持的限制资源2.2 配额作用域2.3 资源配额选型2.3.1 计算资源配额2.3.2 存储资源配额2.3.3 对象数量配额 三、资源限制 LimitRange3.1 限制资源大小值3.2 设置限制默认值3.3 限…

buu [NPUCTF2020]共 模 攻 击 1

题目描述: task: hint: 题目分析&#xff1a; 先看hint(提示)这一部分&#xff0c;标题已经提示了是共模攻击&#xff0c;看到有e1,e2,c1,c2,n也可以想到是共模攻击&#xff0c;之后得到c&#xff0c;继续往下做 此时有点不知如何下手&#xff0c;e 256 并且 gcd(e // 4,p…

接口测试工具Postman接口测试图文教程(超详细)

目录 一、前言 二、Postman安装和使用 三、请求方式 四、资金记录接口实例演示 一、前言 在前后端分离开发时&#xff0c;后端工作人员完成系统接口开发后&#xff0c;需要与前端人员对接&#xff0c;测试调试接口&#xff0c;验证接口的正确性可用性。而这要求前端开发进度…

games103——作业4

实验四主要使用 Shallow Wave 模拟流体 完整项目已上传至github。 文章目录 Height Feild(高度场)更新高度场更新速度场 Shallow Wave EquationDiscretization(离散化)一阶导数二阶导数 Discretized Shallow Wave EquationSolution 1Solution 2Pressure(压强)Viscosity(粘滞) 算…

​性能测试基础——性能测试方案

前面所说的测试分析等准备工作实际上最终目的是制定测试方案&#xff0c;测试方案一般包括&#xff1a; 项目的简要说明、项目系统结构、项目的业务结构、以及项目的性能需求、测试环境数据以及测试策略方法、测试案例、测试人员进度安排以及测试风险预估等等。 下面是一个一般…

FreeRTOS创建静态任务教程及所遇到的问题解决方法

静态任务和动态任务的区别 相对于动态任务&#xff0c;静态任务不需要动态分配内存&#xff0c;而是手动指定一个静态内存缓冲区&#xff0c;并在任务生命周期中一直使用该缓冲区。这可以避免动态内存分配时可能出现的内存碎片和内存泄漏问题&#xff0c;提高了系统的稳定性。…

【Python lxml、BeautifulSoup和html.parser区别介绍】零基础也能轻松掌握的学习路线与参考资料

区别介绍 &#xff08;1&#xff09;lxml lxml是Python的一个XML解析库&#xff0c;它基于libxml2和libxslt库构建&#xff0c;可以读取、操作和输出XML文档。lxml具有很强的性能和稳定性&#xff0c;在处理较大的XML文件时表现尤佳&#xff0c;并且支持XPath、CSS选择器等高…

PHP复习资料(未完待续)

&#xff08;未完待续&#xff0c;请持续关注此板块&#xff09; 【计科三四】雪课堂PHP期末模拟题&#xff1a;https://ks.wjx.top/vm/tUAmjxq.aspx# 【计科一二】PHP第一章练习题 https://ks.wjx.top/vm/QnjHad4.aspx# 【计科一二】PHP第二章练习题 https://ks.wjx.top/vm/h2…

ggplot绘制带误差棒、置信区间的柱状图,并调整颜色为渐变

ggplot绘制带误差棒、置信区间的柱状图,并调整颜色为渐变 简单绘制柱状图控制柱状宽度,间距调整颜色渐变简单绘制柱状图 要在ggplot中绘制带有置信区间的柱状图,你可以使用geom_bar和geom_errorbar函数来完成。下面是一个示例代码: library(ggplot2)# 创建一个示例数据集…

【LeetCode热题100】打卡第1天:两数之和

文章目录 两数之和⛅前言&#x1f512;题目&#x1f511;题解 两数之和 ⛅前言 大家好&#xff0c;我是知识汲取者&#xff0c;欢迎来到我们的LeetCode热题100刷题专栏&#xff01; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合初识算法与…