socket:数据报套接字
作者:互联网
数据报套接字
一、什么是数据报套接字
-
socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。也因为这样,套接字明确地将客户端和服务器区分开来。
-
相对于流套接字,数据报套接字的使用更为简单,它是由类型SOCK_DGRAM指定的,它不需要建立连接和维持一个连接,它们在AF_INET中通常是通过UDP/IP协议实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它并一需要总是要建立和维持一个连接。
二、基于流套接字的客户/服务器的工作流程
1、服务器
-
与使用流套接字一样,首先服务器应用程序用系统调用socket()来创建一个套接安,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。
-
接下来,服务器进程会给套接字起个名字(监听),我们使用系统调用bind()来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。
-
不同的是,然后系统调用recvfrom()来接收来自客户程序发送过来的数据。服务器程序对数据进行相应的处理,再通过系统调用sendto()把处理后的数据发送回客户程序。
与流套接字程序相比:
- 在流套接字中的程序中,接收数据是通过系统调用read,而发送数据是通过系统调用write()来实现,而在数据报套接字程序中,这是通过recvfrom()和sendto()调用来实现的。
使用数据报套接字的服务器程序并不需要listen()调用来创建一个队列来存储连接,也不需要accept()调用来接收连接并创建一个新的socket描述符
2、客户端
- 基于数据报socket的客户端比服务器端简单,同样,客户应用程序首先调用socket来创建一个未命名的套接字,与服务器一样,客户也是通过sendto()和recvfrom()来向服务器发送数据和从服务器程序接收数据。
与流套接字程序相比:
- 使用数据报套接字的客户程序并不需要使用connect()系统调用来连接服务器程序,它只要在需要时向服务器所监听的IP端口发送信息和接收从服务器发送回来的数据即可。
三、套接字接口
- 通用套接字接口见另一篇博客——流套接字
1、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所指向的变量中。
2、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。
四、示例
server.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';
// 发送处理后的数据
sendto(server_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client_addr, client_len);
printf("%c\n", buffer[0]);
// 注意,一定要关闭子进程,否则程序运行会不正常
exit(0);
}
}
// 关闭套接字
close(server_sockfd);
}
client.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);
}
标签:addr,buffer,len,server,sockfd,接字,报套,socket 来源: https://blog.csdn.net/weixin_47803347/article/details/120713254