Linux进程间通信――使用数据报套接字
睿丰德科技 专注RFID识别技术和条码识别技术与管理软件的集成项目。质量追溯系统、MES系统、金蝶与条码系统对接、用友与条码系统对接
前一篇文章,Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用。
一、简单回顾——什么是数据报套接字。
socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。也因为这样,套接字明确地将客户端和服务器区分开来。
相对于流套接字,数据报套接字的使用更为简单,它是由类型SOCK_DGRAM指定的,它不需要建立连接和维持一个连接,它们在AF_INET中通常是通过UDP/IP协议实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它并一需要总是要建立和维持一个连接。
二、基于流套接字的客户/服务器的工作流程
使用数据报socket进行进程通信的进程采用的客户/服务器系统是如何工作的呢?
1、服务器端
与使用流套接字一样,首先服务器应用程序用系统调用socket来创建一个套接安,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。
接下来,服务器进程会给套接字起个名字(监听),我们使用系统调用bind来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。
不同的是,然后系统调用recvfrom来接收来自客户程序发送过来的数据。服务器程序对数据进行相应的处理,再通过系统调用sendto把处理后的数据发送回客户程序。
与流套接字程序相比:
1、在流套接字中的程序中,接收数据是通过系统调用read,而发送数据是通过系统调用write来实现,而在数据报套接字程序中,这是通过recvfrom和sendto调用来实现的。
2、使用数据报套接字的服务器程序并不需要listen调用来创建一个队列来存储连接,也不需要accept调用来接收连接并创建一个新的socket描述符
2、客户端
基于数据报socket的客户端比服务器端简单,同样,客户应用程序首先调用socket来创建一个未命名的套接字,与服务器一样,客户也是通过sendto和recvfrom来向服务器发送数据和从服务器程序接收数据。
与流套接字程序相比:
使用数据报套接字的客户程序并不需要使用connect系统调用来连接服务器程序,它只要在需要时向服务器所监听的IP端口发送信息和接收从服务器发送回来的数据即可。
三、数据报socket的接口及作用
socket的接口函数声明在头文件sys/types.h和sys/socket.h中。
1、创建套接字——socket系统调用
该函数用来创建一个套接字,并返回一个描述符,该描述符可以用来访问该套接字,它的原型如下:
[cpp] view plaincopyprint?
- int socket(int domain, int type, int protocol);
- int bind( int socket, const struct sockaddr *address, size_t address_len);
- int sendto(int sockfd, void *buffer, size_t len, int flags, struct sockaddr *to, socklen_t tolen);
- int recvfrom(int sockfd, void *buffer, size_t len,int flags, struct sockaddr *src_from, socklen_t *src_len);
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <signal.h>
- int main()
- {
- int server_sockfd = -1;
- int server_len = 0;
- int client_len = 0;
- char buffer[512];
- int result = 0;
- struct sockaddr_in server_addr;
- struct sockaddr_in client_addr;
- //创建数据报套接字
- server_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- //设置监听IP端口
- server_addr.sin_family = AF_INET;
- server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
- server_addr.sin_port = htons(9739);
- server_len = sizeof(server_addr);
- //绑定(命名)套接字
- bind(server_sockfd, (struct sockaddr*)&server_addr, server_len);
- //忽略子进程停止或退出信号
- signal(SIGCHLD, SIG_IGN);
- while(1)
- {
- //接收数据,用client_addr来储存数据来源程序的IP端口
- result = recvfrom(server_sockfd, buffer, sizeof(buffer), 0,
- (struct sockaddr*)&client_addr, &client_len);
- if(fork() == 0)
- {
- //利用子进程来处理数据
- buffer[0] += 'a' - 'A';
- sleep(5);
- //发送处理后的数据
- sendto(server_sockfd, buffer, sizeof(buffer),0 ,
- (struct sockaddr*)&client_addr, client_len);
- printf("%c\n", buffer[0]);
- //注意,一定要关闭子进程,否则程序运行会不正常
- exit(0);
- }
- }
- //关闭套接字
- close(server_sockfd);
- }
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <stdlib.h>
- #include <stdio.h>
- int main(int agrc, char *argv[])
- {
- struct sockaddr_in server_addr;
- int server_len = 0;
- int sockfd = -1;
- int result = 0;
- char c = 'A';
- //取第一个参数的第一个字符
- if(agrc > 1)
- c = argv[1][0];
- //创建数据报套接字
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- //设置服务器IP端口
- server_addr.sin_family = AF_INET;
- server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- server_addr.sin_port = htons(9739);
- server_len = sizeof(server_addr);
- //向服务器发送数据
- sendto(sockfd, &c, sizeof(char), 0,
- (struct sockaddr*)&server_addr, server_len);
- //接收服务器处理后发送过来的数据,由于不关心数据来源,所以把后两个参数设为0
- recvfrom(sockfd, &c, sizeof(char), 0, 0, 0);
- printf("char from server = %c\n", c);
- //关闭套接字
- close(sockfd);
- exit(0);
- }