学习视频链接
目录
一、UDP 和 TCP 的优缺点
1、TCP
面向连接的,可靠数据包传输。对于不稳定的网络层,采取完全弥补的通信方式。丢包重传。
优点:稳定 (数据流量稳定、速度稳定、顺序)
缺点:传输速度慢。相率低。开销大
使用场景:数据的完整型要求较高,不追求效率。大数据传输、文件传输
2、UDP
无连接的,不可靠的数据报传递。对于不稳定的网络层,采取完全不弥补的通信方式。默认还原网络状况
优点:传输速度块。相率高。开销小
缺点:不稳定 (数据流量。速度。顺序)
使用场景:对时效性要求较高场合。稳定性其次。游戏、视频会议、视频电话。腾讯、华为、阿里--- 应用层数据校验协议, 弥补 udp 的不足
二、UDP 实现的 C/S 模型
2.1 通信过程
recv() / send() 只能用于 TCP 通信。替代 read、write
accpet():--- Connect(); ---被舍弃
server:
lfd = socket(AF_INET, STREAM, 0);
SOCK_DGRAM ---报式协议
bind();
listen(); --- 可有可无
while(1) {
read(cfd, buf, sizeof) ---被替换 ---recvfrom() ---涵盖accept传出地址结构体
小 -- 大
write():---被替换--- sendto()
}
close();
client:
connfd = socket(AF_INET, SOCK_DGRAI, 0);
sendto(‘服务器的地址结构’, 地址结构大小)
recvfrom()
写到屏幕
close();
2.2 函数讲解
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
sockfd: lfd
buf: 缓冲区地址
len: 缓神区大小
flags: 0
src_addr: (struct sockaddr *) &addr 传出。对端地址结构
addrlen: 传入传出
返回值: 成功接收数据字节数。失败: -1 errn。 0: 对端关闭
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
sockfd: 套接字
buf: 存储数据的缓冲区
len: 数据长度
flags: 0
src_addr: (struct sockaddr *) &addr 传入。目标地址结构
addrlen: 地址结构长度
返回值:成功接收数据字节数。失败 -1 errno
2.3 代码
server.c
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>
#define SERV_PORT 8000
int main(void)
{
struct sockaddr_in serv_addr, clie_addr;
socklen_t clie_addr_len;
int sockfd;
char buf[BUFSIZ];
char str[INET_ADDRSTRLEN];
int i, n;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_PORT);
bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
printf("Accepting connections ...\n");
while (1) {
clie_addr_len = sizeof(clie_addr);
n = recvfrom(sockfd, buf, BUFSIZ, 0, (struct sockaddr *)&clie_addr, &clie_addr_len);
if (n == -1) {
perror("recvfrom error");
}
printf("received from %S at PORT %d\n",
inet_ntop(AF_INET, &clie_addr.sin_addr, str, sizeof(str)),
ntohs(clie_addr.sin_port));
for(i = 0; i < n; i++) {
buf[i] = toupper(buf[i]);
}
n = sendto(sockfd, buf, n, 0, (struct sockaddr *)&clie_addr, sizeof(clie_addr));
if (n == -1) {
perror("sendto error");
}
}
close(sockfd);
return 0;
}
client.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>
#define SERV_PORT 8000
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr;
int sockfd, n;
char buf[BUFSIZ];
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
while (fgets(buf, BUFSIZ, stdin) != NULL) {
n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
if (n == -1)
perror("sendto error");
n = recvfrom(sockfd, buf, BUFSIZ, 0, NULL, 0); //NULL:不关心对端信息
if (n == -1)
perror("recvfrom error");
write(STDOUT_FILENO, buf, n);
}
close(sockfd);
return 0;
}
三、本地套接字和网络套接字
3.1 进程间通信方式 (IPC)
pipe:易用性是最强的
fifo:没有血缘关系进程之间的通信
mmap:没有血缘关系进程之间的通信,可以反复读取
信号:开销最小
本地套接字 (domain):稳定性最好的(CS 模型)
3.2 本地套接字对比网络 TCP
1、int socket(int domain, int type, int protocol);
参数:
domin: AF_INET 变为 AF_UNIX/AF_LOCAL
type: SOCK_STREAM/SOCK_DGRAM 都可以
2、地址结构 socke_in -> sockaddr_un
struct sockaddr_in srv_addr; -> struct sockaddr_un srv_addr;
srv_addr.sin_family = AF_INET; -> srv_adrr.sun_family = AF_UNIX;
srv_addr.sin_port = htons(8888); -> strcpy(srv_addr.sun_path, "srv.socket")
srv_addr.sin_addr.s_addr = hton(INADDR_ANY);
bind(fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr)); -> bind(fd, (struct sockaddr *)&srv_addr, offsetof(struct sockaddr_un, sun_path) + strlen("srv.socket"));
sockaddr_un
sockaddr 数据结构
bind 函数创建出来的这个文件,是一个伪文件
3.3 代码
服务端
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <stddef.h>
#include "wrap.h"
#define SERV_ADDR "serv.socket"
int main(void)
{
int lfd, cfd, len, size, i;
struct sockaddr_un servaddr, cliaddr;
char buf[4096];
lfd = Socket(AF_UNIX, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path, SERV_ADDR);
len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path); /* servaddr total len */
unlink(SERV_ADDR); /* 确保bind之前serv.sock文件不存在,bind会创建该文件 */
Bind(lfd, (struct sockaddr *)&servaddr, len); /* 参3不能是sizeof(servaddr) */
Listen(lfd, 20);
printf("Accept ...\n");
while (1) {
len = sizeof(cliaddr); //AF_UNIX大小+108B
cfd = Accept(lfd, (struct sockaddr *)&cliaddr, (socklen_t *)&len);
len -= offsetof(struct sockaddr_un, sun_path); /* 得到文件名的长度 */
cliaddr.sun_path[len] = '\0'; /* 确保打印时,没有乱码出现 */
printf("client bind filename %s\n", cliaddr.sun_path);
while ((size = read(cfd, buf, sizeof(buf))) > 0) {
for (i = 0; i < size; i++)
buf[i] = toupper(buf[i]);
write(cfd, buf, size);
}
close(cfd);
}
close(lfd);
return 0;
}
客户端
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <stddef.h>
#include "wrap.h"
#define SERV_ADDR "serv.socket"
#define CLIE_ADDR "clie.socket"
int main(void)
{
int cfd, len;
struct sockaddr_un servaddr, cliaddr;
char buf[4096];
cfd = Socket(AF_UNIX, SOCK_STREAM, 0);
bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sun_family = AF_UNIX;
strcpy(cliaddr.sun_path,CLIE_ADDR);
len = offsetof(struct sockaddr_un, sun_path) + strlen(cliaddr.sun_path); /* 计算客户端地址结构有效长度 */
unlink(CLIE_ADDR);
Bind(cfd, (struct sockaddr *)&cliaddr, len); /* 客户端也需要bind, 不能依赖自动绑定*/
bzero(&servaddr, sizeof(servaddr)); /* 构造server 地址 */
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path, SERV_ADDR);
len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path); /* 计算服务器端地址结构有效长度 */
Connect(cfd, (struct sockaddr *)&servaddr, len);
while (fgets(buf, sizeof(buf), stdin) != NULL) {
write(cfd, buf, strlen(buf));
len = read(cfd, buf, sizeof(buf));
write(STDOUT_FILENO, buf, len);
}
close(cfd);
return 0;
}
3.4 总结
对比 本地套 和 网络套