前一篇文章,Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用。

一、简单回顾——什么是数据报套接字

socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。也因为这样,套接字明确地将客户端和服务器区分开来。

相对于流套接字,数据报套接字的使用更为简单,它是由类型SOCK_DGRAM指定的,它不需要建立连接和维持一个连接,它们在AF_INET中通常是通过UDP/IP协议实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它并一需要总是要建立和维持一个连接。

二、基于流套接字的客户/服务器的工作流程

使用数据报socket进行进程通信的进程采用的客户/服务器系统是如何工作的呢?

1、服务器端

与使用流套接字一样,首先服务器应用程序用系统调用socket()来创建一个套接安,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。

接下来,服务器进程会给套接字起个名字(监听),我们使用系统调用bind()来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。

不同的是,然后系统调用recvfrom()来接收来自客户程序发送过来的数据。服务器程序对数据进行相应的处理,再通过系统调用sendto()把处理后的数据发送回客户程序。

与流套接字程序相比:

  • 在流套接字中的程序中,接收数据是通过系统调用read,而发送数据是通过系统调用write()来实现,而在数据报套接字程序中,这是通过recvfrom()和sendto()调用来实现的。
  • 使用数据报套接字的服务器程序并不需要listen()调用来创建一个队列来存储连接,也不需要accept()调用来接收连接并创建一个新的socket描述符

2、客户端

基于数据报socket的客户端比服务器端简单,同样,客户应用程序首先调用socket来创建一个未命名的套接字,与服务器一样,客户也是通过sendto()和recvfrom()来向服务器发送数据和从服务器程序接收数据。

与流套接字程序相比:

使用数据报套接字的客户程序并不需要使用connect()系统调用来连接服务器程序,它只要在需要时向服务器所监听的IP端口发送信息和接收从服务器发送回来的数据即可。

三、数据报socket的接口及作用

socket的接口函数声明在头文件 sys/types.h 和 sys/socket.h 中。

1、创建套接字——socket()系统调用

该函数用来创建一个套接字,并返回一个描述符,该描述符可以用来访问该套接字,它的原型如下:

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

函数中的三个参数分别对应前面所说的三个套接字属性。protocol参数设置为0表示使用默认协议。

2、命名(绑定)套接字——bind()系统调用

该函数把通过socket()调用创建的套接字命名,从而让它可以被其他进程使用。对于AF_UNIX,调用该函数后套接字就会关联到一个文件系统路径名,对于AF_INET,则会关联到一个IP端口号。函数原型如下:

int bind( int socket, const struct sockaddr *address, size_t address_len);

成功时返回0,失败时返回-1;

3、发送数据——sendto()系统调用

该函数把缓冲区buffer中的信息给送给指定的IP端口的程序,原型如下:

int sendto(int sockfd, void *buffer, size_t len, int flags, struct sockaddr *to, socklen_t tolen);

buffer中储存着将要发送的数据,len是buffer的长度,而flags在应用中通常被设置为0,to是要发送数据到的程序的IP端口,tolen是to参数的长度。

成功时返回发送的数据的字节数,失败时返回-1。

4、接收数据——recvfrom()系统调用

该函数把发送给程序的信息储存在缓冲区buffer中,并记录数据来源的程序IP端口,原型如下:

int recvfrom(int sockfd, void *buffer, size_t len,int flags, struct sockaddr *src_from, socklen_t *src_len);

buffer用于储存接收到的数据,len指定buffer的长度,而flags在应用中通常被设置0,src_from若不为空,则记录数据来源程序的IP端口,若src_len不为空,则其长度信息记录在src_len所指向的变量中。

注意:默认情况下,recvfrom()是一个阻塞的调用,即直到它接收到数据才会返回。

5、关闭socket——close()系统调用

该系统调用用来终止服务器和客户上的套接字连接,我们应该总是在连接的两端(服务器和客户)关闭套接字。

四、进程使用数据报socket进行通信

下面用多个客户程序实例和一个服务器程序来演示多个进程如何通过使用数据报socket来进行通信。

sockserver2.c是一个服务器程序,它接收客户程序发来的数据,并创建一个子进程来处理客户发送过来的数据,处理过程非常简单,就是把大写字母改为小写。然后把处理后的数据(大写字母对应的小写字母)发送回给客户端。

sockclient2.c是一个客户程序,它向服务器程序发送数据,并接收服务器发送过来的处理后的数据(即小写字母),然后把接收到的数据输出到屏幕上。在运行客户程序时,你可以为它提供一个字符作为参数,此时客户程序将把些字符作为要处理的数据发送给服务器,如果不提供一个参数,则默认发送字符A。

sockserver2.c的源代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h> int main(int arc, char **argv)
{
int server_sockfd = -1;
socklen_t server_len = 0;
socklen_t client_len = 0;
char buffer[512];
ssize_t 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(1); // 发送处理后的数据
sendto(server_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client_addr, client_len); printf("%c\n", buffer[0]); // 注意,一定要关闭子进程,否则程序运行会不正常
exit(0);
}
} // 关闭套接字
close(server_sockfd);
}

sockclient2.c的源代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> #include <netinet/in.h>
#include <arpa/inet.h> int main(int argc, char **argv)
{
struct sockaddr_in server_addr;
socklen_t server_len = 0;
int sockfd = -1;
char c = 'A'; // 取第一个参数的第一个字符
if (argc > 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);
}

运行结果如下:

先运行服务器程序,如下:

再运行三个客户端:如下:

在本例子中,我们启动了一个服务器程序和三个客户程序,从运行的结果来看,客户端发送给服务器程序的所有请求都得到了处理,即把大写字母变成了小写。recvfrom()调用是阻塞的调用,即只有当接收到数据才会返回。

五、数据报套接字与流套接字的比较

1、从使用的便利和效率来讲

我们可以看到使用数据报套接字的确是比使用流套接字简单,而且快速。

因为使用流套接字的程序,客户程序需要调用connect()来创建一个到服务器程序的连接,并需要维持这个连接,服务器程序也需要调用listen()来创建一个队列来保存未处理的请求,当有数据到达时,服务器也不需要调用accept()来接受连接并创建一个新socket描述符来处理请求。

再来看看使用数据报套接字的程序,服务器程序与客户程序所使用的系统调用大致相同,服务器程序只比客户程序多使用了一个bind()调用。基于数据报套接字的程序,只需要使用sendto()调用来向指定IP端口的程序发送信息,使用recvfrom()调用从指向的IP端口接收信息即可。因为它并不需要建立一个连接,接受连接等,所以省去了很多的功夫。

2、从使用场合来讲

我们知道流套接字是基于TCP/IP协议的,它是一种安全的协议,提供的是一个有序、可靠、双向字节流的连接,发送的数据可以确保不会丢失、重复或乱序到达,而且它还有一定的出错后重新发送的机制。所以它比较适合用来发送信息量大的数据文件,或对数据完整性要求较高的文件,如压缩文件、视频文件等

而数据报套接字是基于UDP/IP协议实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高。所以它比较适合发送一些对实时性要求较高,但是对安全性和完整性要求不太高的数据。如我们熟悉的聊天信息,即使有一点的丢失也不会造成理解上的大的问题。

参考:

http://blog.csdn.net/ljianhui/article/details/10697935

Linux进程间通信(九):数据报套接字 socket()、bind()、sendto()、recvfrom()、close()的更多相关文章

  1. Linux进程间通信——使用数据报套接字

    前一篇文章, Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用. 一.简单回顾——什么是数据报 ...

  2. Linux进程间通信——使用流套接字

    前面说到的进程间的通信,所通信的进程都是在同一台计算机上的,而使用socket进行通信的进程可以是同一台计算机的进程,也是可以是通过网络连接起来的不同计算机上的进程.通常我们使用socket进行网络编 ...

  3. IPC——数据报套接字通信

    Linux进程间通信——使用数据报套接字 前一篇文章,Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接 ...

  4. Linux进程间通信(八):流套接字 socket()、bind()、listen()、accept()、connect()、read()、write()、close()

    前面说到的进程间的通信,所通信的进程都是在同一台计算机上的,而使用socket进行通信的进程可以是同一台计算机的进程,也是可以是通过网络连接起来的不同计算机上的进程.通常我们使用socket进行网络编 ...

  5. [Socket网络编程]由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。

    原文地址:http://blog.sina.com.cn/s/blog_70bf579801017ylu.html,记录在此方便查看 解决办法: MSDN的说明: Close 方法可关闭远程主机连接, ...

  6. socket 错误之:OSError: [WinError 10057] 由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。

    出错的代码 #server端 import socket import struct sk=socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen( ...

  7. Linux网络通信(TCP套接字编写,多进程多线程版本)

    预备知识 源IP地址和目的IP地址 IP地址在上一篇博客中也介绍过,它是用来标识网络中不同主机的地址.两台主机进行通信时,发送方需要知道自己往哪一台主机发送,这就需要知道接受方主机的的IP地址,也就是 ...

  8. pythonl练习笔记——PythonNet 套接字socket

    1 套接字socket 1.1 套接字概述 套接字,一种网络通讯工具:用于进行网络间的通信,是一种特殊文件类型, 套接字,是一个通信链的句柄,用于描述IP地址和端口,实现向网络发出请求或应答网络请求. ...

  9. Linux网络编程之套接字基础

    1.套接字的基本结构 struct sockaddr 这个结构用来存储套接字地址. 数据定义: struct sockaddr { unsigned short sa_family; /* addre ...

随机推荐

  1. NopCommerce 增加 Customer Settings

    预期: 仿照Customer 的 Phone number enabled 和 required 增加MemberType 相关步骤如下: 1.运行站点 Admin -> Settings -& ...

  2. win7如何恢复以前的ie版本

    如何恢复以前的ie版本-控制面板,程序和功能-查看已安装的更新-搜索Internet explorer,然后卸载更新就Ok.

  3. 第14章 集合框架(1)-List集合的各种类

    1.概述 1.1.Java集合框架的由来 1.2.什么是集合框架? 1.3.为什么需要集合框架 1.4.常用的框架接口规范 2.Vector类 2.1.存储原理 2.2.构造方法 2.3.常用方法 3 ...

  4. python-网络编程-socket编程

    一.TCP\IP(网络通信协议)简介       TCP:(Transmission Control Protocol)传输控制协议,面向有连接的通信协议  UDP:数据报文协议,面向无连接的通信协议 ...

  5. 提高Axure设计效率的10条建议

    http://www.woshipm.com/ucd/92153.html Axure 是创建软件原型的快速有力的工具.上手很容易,但是,其中存在一个危险.这款软件是如此的直观以至于很多用户可以在没有 ...

  6. linux下vi命令大全

    进入vi的命令vi filename :打开或新建文件,并将光标置于第一行首vi +n filename :打开文件,并将光标置于第n行首vi + filename :打开文件,并将光标置于最后一行首 ...

  7. # 2015-2016-2 《Java程序设计》课程总结

    2015-2016-2 <Java程序设计>课程总结

  8. .NET跨平台之旅:将示例站点从ASP.NET 5 Beta5升级至Beta7

    9月2日,微软发布了ASP.NET 5 Beta7(详见Announcing Availability of ASP.NET 5 Beta7).其中最大的亮点是dnx已经可以完全基于CoreCLR运行 ...

  9. Go学习

    简介 Go语言是Google出了一个语言,基本概念我就不介绍了, GO语言从原生上支持高并发,并提供了简单的调用方式,我们就重点研究一下它的高并发 进程与线程 在介绍高并发之前,我们需要了解一下我们现 ...

  10. 关于Unity的网络框架

    注:Unity 5.1里引入了新的网络框架,未来目标应该是WOW那样的,现在还只是个P2P的架子. 网络的框架,无非是如何管理网络数据的收发,通信双方如何约定协议.之前做的框架与GameObject无 ...