• Linux IPC tcp/ip socket 编程


    模型

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    //服务器:  
    
    socket()              //创建socket					
    struct sockaddr_in    //准备通信地址		
    bind()                //绑定socket和addr
    listen()              //创建listening socket
    accept()              //创建connect socket
    send()/recv()         //进行通信			
    close()               //关闭socket			
    
    //客户端:
    
    socket()              //创建socket
    准备通信地址:服务器的地址
    connect()             //链接socket和通信地址
    send()/recv()         //进行通信
    close()               //关闭socket
    

    socket()

    //创建网络端点,返回socket文件描述符,失败返回-1设errno
    int socket(int domain, int type, int protocol);
    

    domain :协议族(protocol family)(网络通讯(IP)还是本地通讯(xxx.socket))

    • AF_INET用于实现给予ipv4网络协议的网络协议

    type :协议(TCP还是UDP)

    • SOCK_STREAM //流式套接字, 实现包括但不限于TCP协议,which is有序,可靠双向面向连接的字节流通信方式

    protocol: 特殊协议, 一般给0

    准备通信地址:

    struct sockaddr{	//主要用于函数的形参类型, 很少定义结构体变量使用, 叫做通用的通信地址类型//$man bind
    	sa_family_t 	sa_family;
    	char        	sa_data[14];
    }
    struct sockaddr_in{	//准备网络通信的通信地址	//$man in.h
    	sa_family_t	sin_family;   	//协议族, 就是socket()的domain的AF_INET
    	in_port_t       sin_port;   //端口号
    	struct in_addr	sin_addr;   //IP地址
    }
    struct in_addr{	
        in_addr_t	s_addr;		//整数类型的IP地址
    }
    

    bind():

    //把通信地址和socket文件描述符绑定,用在服务器端,成功返回0,失败返回-1设errno
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    

    sockfd: socket文件的fd(returned by socket())
    addr: 需要强制类型转换成socketaddr_un或soketaddr_in, 参见上
    addrlen: 通信地址的大小, 使用sizeof();

    listen()

    //创建侦听socket,把sockfd标记的socket标记为被动socket,被动socket表示这个socket只能用来接收即将到来的连接请求,不再用于读写操作和通信,接收连接请求的是accept()
    //成功返回0,失败返回-1设errno
    int listen(int sockfd, int backlog);
    

    backlog:排队等待“被响应”连接请求队列的最大长度 eg: 接待室的最大长度

    accept()

    //创建连接socket,返回连接socket的文件描述符,成功返回文件描述符,失败返回-1设errno
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    

    addr : 结构体指针, 用于带出客户端的通信地址
    addlen : 结构体指针, 用于带出通信地址的大小
    ATTENTION: listen()把socket()创建的sockfd变为listening socket, 负责侦听哪个client连接上了(即不但要知道连上没, 还要知道谁连上了, 这个SOCK_STREAM的socket有这个能力), accept()提取排队中的最上面的一个client, 给它一个conneted socket, 这样这个client就可以和server沟通了, 就是说这里有两个socket, 一个负责侦听一个负责通信

    send()

    //向指定的socket发送指定的数据,成功返回实际发送数据的大小,失败返回-1设errno
    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    

    sockfd: 用于通信的socket描述符(returned by accept())
    buf : 被发送数据的缓冲区首地址
    len : 被发送数据的大小
    flags: 发送的标志, 如果给0等同于write()

    recv()

    //从指定的socket接收数据,成功返回接收的数据的大小,失败返回-1设errno
    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    

    sockfd: 用于通信的socket描述符(returned by accept())
    buf: 接收数据的缓冲区首地址
    len: 接收数据的大小
    flags: 发送的标志, 如果给0等同于read()

    connect():

    //初始化一个socket的连接,用在客户端,成功返回0,失败返回-1设errno
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    

    sockfd: socket文件的fd(returned by socket())
    addr: 需要强制类型转换成socketaddr_un或soketaddr_in, 参见上
    addrlen: 通信地址的大小, 使用sizeof();

    例子-多进程并发tcp/ip协议服务器模型

    除了这种多进程的并发模型,还有多线程并发和I/O多路复用并发等方式

    //创建server, 用多进程同时响应多个client的请求, 当client发来 “bye”的时候断开连接, 按下Ctrl+C关闭服务器
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>		//省略了几个头文件
    int sockfd;	//全局变量
    
    void fa(int signo){
    	printf("closing server...
    ");
    	sleep(3);
    	int res=close(sockfd);
    	if(-1==res)
    		perror("close"),exit(-1);
    	printf("server closed
    ");
    	exit(0);
    }
    int main(){
    	//1.创建socket	socket()
    	int sockfd=socket(AF_INET,SOCK_STREAM,0);
    	if(-1==sockfd)
    		perror("socket"),exit(-1);
    	printf("creat socket success
    ");
    
    	//2.准备通信地址(服务器地址),使用结构体类型
    	struct sockaddr_in addr={0};    //要初始化为0
    	addr.sin_family=AF_INET;
    	addr.sin_port=htons(8888);
    	addr.sin_addr.s_addr=inet_addr("176.43.11.211");				//client也用这个server的地址
    
    	//3.绑定socket和通信地址,使用bind()
    	int res=bind(sockfd, (struct sockaddr*)&addr,sizeof(addr));			//client用connect
    	if(-1==res)
    		perror("bind"),exit(-1);
    	printf("bind success
    ");
    	
    	//4.生成侦听socket:   listening socket	listen()					//client可没有这一步
    	res=listen(sockfd,100);
    	if(-1==res)
    		perror("listen"),exit(-1);
    	printf("listen success
    ");
    	//set SIGINT
    	printf("Press ctrl+c to close server
    ");
    	if(SIG_ERR==signal(SIGINT,fa))		//整个程序,包括第一个while(1)是通过信号处理终止的
    		perror("signal"),exit(-1);
     
    	while(1){	//只要有client接入就创建新进程与之通信
    		struct sockaddr_in recv_addr;
    		socklen_t len=sizeof(recv_addr);
    		int CnnSockfd=accept(sockfd,(struct sockaddr*)&recv_addr,&len);		
    			//如果侦听队列里面有client就accept(), 否则就在这阻塞着,不继续执行,除非遇到Ctrl+C终止整个进程
    		if(-1==CnnSockfd)
    			perror("accept"),exit(-1);
    		char *ip=inet_ntoa(recv_addr.sin_addr);
    		printf("client:%s linked
    ",ip);
    		
    		pid_t pid=fork();
    		if(-1==pid)
    			perror("fork()"),exit(-1);
    		if(0==pid){
    			if(SIG_ERR==signal(SIGINT,SIG_DFL))
    				perror("signal"),exit(-1);
                //每个child处理一个client,所以已经不需要listening socket了,可以把它关了
                //如果不关子进程也会有一个listenfd,会和父进程一起抢,不应该
    			res=close(sockfd);	
    			if(-1==res)
    				perror("close"),exit(-1);
    			while(1){			//只要client发数据就处理,除非遇到 “bye”
    				char buf[100]={0};
    				res=recv(CnnSockfd,buf,sizeof(buf),0);
    				if(-1==res)
    					perror("recv"),exit(-1);
    				printf("client%s,data sent:%s
    ",ip,buf);
    				if(!strcmp(buf,"bye")){		//遇到“bye”就不再待命,break掉准备断开连接
    					printf("client%s has been unlinked
    ",ip);
    					break;
    				}
    				res=send(CnnSockfd,"I received!",12,0);
    				if(-1==res)
    					perror("send"),exit(-1);
    			}
    			res=close(CnnSockfd);			//断开连接即close(相应的connected socket)
    			if(-1== res)
    				perror("close"),exit(-1);
    			exit(0);						//断开了连接了,就可以exit子进程了
    		}
    		res=close(CnnSockfd);	//
    		if(-1==res)
    			perror("close"),exit(-1);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Spring Boot 使用 Micrometer 集成 Prometheus 监控 Java 应用性能
    Prometheus 通过 consul 实现自动服务发现
    Prometheus 通过 consul 分布式集群实现自动服务发现
    使用 PushGateway 进行数据上报采集
    AlertManager 之微信告警模板,UTC时间错8个小时的解决办法
    Prometheus 监控报警系统 AlertManager 之邮件告警
    Elasticsearch:使用 IP 过滤器限制连接
    Elasticsearch:创建 API key 接口访问 Elasticsearch
    Elasticsearch:反向代理及负载均衡在 Elasticsearch 中的应用
    Kibana:如何周期性地为 Dashboard 生成 PDF Report
  • 原文地址:https://www.cnblogs.com/xiaojiang1025/p/5950458.html
Copyright © 2020-2023  润新知