1.UDP框架
1.1服务器
socket --- 创建套接字
SOCK_STREAM -- 流式套接字 TCP
SOCK_DGRAM -- 数据报套接字 UDP(这个是socket创建套接字的函数参数列表中,type的选项:
int socket(int domain, int type, int protocol);
type
:指定套接字的通信类型,例如SOCK_STREAM
(TCP)或SOCK_DGRAM
(UDP))
bind --- 绑定本机地址和端口 recvfrom --- 接收数据并获取数据来自哪里
sendto --- 指定向哪里发送数据
1.2客户端
socket --- 创建套接字
SOCK_STREAM -- 流式套接字 TCP
SOCK_DGRAM -- 数据报套接字 UDP
bind(可选) --- 绑定本机地址和端口
sendto --- 指定向哪里发送数据 recvfrom --- 接收数据并获取数据来自哪里
2.接口函数
在TCP中,数据的接收与发送函数是recv和send,在UDP中是recvfrom和sendto。
作用:接收数据,并获取数据来自哪里
#include <sys/types.h>
#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
参数:
sockfd ---- 套接字文件描述符
buf ---- 存放接收数据首地址
len ---- 想要接收数据的长度
flags ---- 接收送方式, 0-阻塞接收
src_addr --- 发送方的地址
addrlen --- 发送方地址的长度
返回值:
成功: 成功接收的字节数
失败: -1,并设置errno
作用: 指定向目标发送数据
#include <sys/types.h>
#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
参数:
sockfd ---- 套接字文件描述符
buf ---- 发送数据首地址
len ---- 发送数据的长度
flags ---- 发送方式, 0-阻塞发送
dest_addr --- 发送的目标地址
addrlen --- 地址长度
返回值:
成功: 发送成功的字节数
失败: -1,并设置errno
3.代码示例
3.1头文件
#ifndef __NET_H__ #define __NET_H__ #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> typedef struct sockaddr SA; typedef struct sockaddr_in SAI; #endif
3.2服务器代码
#include <stdio.h> #include "net.h" int main(int argc, char *argv[]) { //1. 创建套接字 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket"); return -1; } printf("socket success!\n"); //2. 绑定本机地址和端口 struct SAI srvaddr = { .sin_family = AF_INET, .sin_port = htons(8686), .sin_addr.s_addr = htonl(INADDR_ANY) //0.0.0.0 }; if (0 > bind(sockfd, (SA *)&srvaddr, sizeof(srvaddr))) { perror("bind"); return -1; } printf("bind success!\n"); char buf[1024]; int ret; SAI cliaddr; int addrlen = sizeof(cliaddr); while (1) { //3. 接收数据 memset(buf, 0, sizeof(buf)); ret = recvfrom(sockfd, buf, sizeof(buf), 0, (SA*)&cliaddr, &addrlen); if (ret < 0) { perror("recvfrom"); break; } printf("Recv: %s data: %s\n", inet_ntoa(cliaddr.sin_addr), buf); //4. 发送数据 ret = sendto(sockfd, buf, ret, 0, (SA*)&cliaddr, addrlen); if (ret < 0) { perror("sendto"); break; } if (strstr(buf, "quit")) break; } //5. 关闭套接字 close(sockfd); return 0; }
3.3客户端代码
#include <stdio.h> #include "net.h" int main(int argc, char *argv[]) { //1. 创建数据报套接字 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket"); return -1; } printf("socket success!\n"); char buf[1024]; int ret; SAI srvaddr = { .sin_family = AF_INET, .sin_port = htons(8686), .sin_addr.s_addr = inet_addr("192.168.6.134") }; while (1) { //2. 发送数据 printf("Send: "); fgets(buf, sizeof(buf), stdin); ret = sendto(sockfd, buf, sizeof(buf), 0, (SA*)&srvaddr, sizeof(srvaddr)); if (ret < 0) { perror("sendto"); break; } //3. 接收数据 memset(buf, 0, sizeof(buf)); ret = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL); if (ret < 0) { perror("recvfrom"); break; } printf("Recv: %s", buf); if (strstr(buf, "quit")) break; } //4. 关闭套接字 close(sockfd); return 0; }
4.后续
下篇学习最常用的服务器模型,以实现一个服务器响应多个客户端的需求。
循环服务器:在同一个时刻只能响应一个客户端的请求
并发服务器: 并发服务器在同一个时刻可以响应多个客户端的请求