一.UDP通信
1.TCP通信和UDP通信各自的优缺点
TCP: 面向连接的,可靠数据包传输。对于不稳定的网络层,采取完全弥补的通信方式。 丢包重传。 优点:
稳定。
数据流量稳定、速度稳定、顺序
缺点:
传输速度慢。相率低。开销大。 使用场景:数据的完整型要求较高,不追求效率。 大数据传输、文件传输。UDP: 无连接的,不可靠的数据报传递。对于不稳定的网络层,采取完全不弥补的通信方式。 默认还原网络状况 优点: 传输速度块。相率高。开销小。 缺点:
不稳定。
数据流量。速度。顺序。 使用场景:对时效性要求较高场合。稳定性其次。 游戏、视频会议、视频电话。 腾讯、华为、阿里 --- 应用层数据校验协议,弥补udp的不足。
2.UDP实现的 C/S 模型
-
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传出地址结构。ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);sockfd: 套接字buf:缓冲区地址len:缓冲区大小flags: 0src_addr:(struct sockaddr *)&addr 传出。 对端地址结构addrlen:传入传出。返回值: 成功接收数据字节数。 失败:-1 errn。 0: 对端关闭。小-- 大write();--- 被替换 --- sendto()---- connectssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);sockfd: 套接字buf:存储数据的缓冲区len:数据长度flags: 0src_addr:(struct sockaddr *)&addr 传入。 目标地址结构addrlen:地址结构长度。返回值:成功写出数据字节数。 失败 -1, errno }close();
client
:
connfd = socket(AF_INET, SOCK_DGRAM, 0); sendto(‘服务器的地址结构’, 地址结构大小) recvfrom() 写到屏幕 close();
3.代码
server.cpp:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>#define SERV_PORT 9527int main()
{int sockfd;sockfd = socket(AF_INET,SOCK_DGRAM,0); //表示选择UDP协议char buf[1024],clie_IP[BUFSIZ];int ret;struct sockaddr_in serv_addr,clie_addr;socklen_t clie_addr_len = sizeof(clie_addr);serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT);serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);bind(sockfd,(struct soakaddr *)&serv_addr,sizeof(serv_addr)); //绑定服务器的信息while(1){ret =n recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&clie_addr,&clie_addr_len);printf("client 's IP:%s port:%d\n", inet_ntop(AF_INET,&clie_addr.sin_addr.s_addr,clie_IP,sizeof(clie_IP)),ntohs(clie_addr.sin_port));for(int i = 0;i<ret;i++)buf[i] = toupper(buf[i]);sendto(socket,buf,ret,0,(struct sockaddr *)*clie_addr,clie_addr_len);}close(sockfd);
}
cilent.cpp:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>#define SERV_PORT 8000int 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;
}
二.本地套接字
1.回顾/了解
进程间通信:
- pipe管道:应用性最强
- FIFO有名管道:可以在没有血缘关系间进行通信
- mmap共享内存:没有血缘关系间通信且可以返回读取
- 信号:开销最小
- 本地套接字(domain):稳定性最好
对比网络编程的TCP的C/S模型,注意以下几点:
函数原型:
#include <sys/socket.h>int socket(int domain, int type, int protocol);
domain
:用来指定传输协议,网络套接字使用AF_INET
和AF_INET6
分别表示IPv4和IPv6,而本地套接字需要使用AF_UNIX
或AF_LOCAL
type
:用来指定协议类型,可以取SOCK_STREAM
表示流式协议或SOCK_DGRAM
表示报式协议protocol
:传0表示默认协议
type为
SOCK_STREAM
且protocol=0表示使用TCP传输,type为SOCK_DGRAM
且protocol=0表示使用UDP传输。
第二步,注意bind函数的地质结构体发生变化,由原来的sockaddr_in
变成了sockaddr_un
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */ 地址结构类型
__be16 sin_port; /* Port number */ 端口号
struct in_addr sin_addr; /* Internet address */ IP地址
};
struct sockaddr_un {
__kernel_sa_family_t sun_family; /* AF_UNIX */ 地址结构类型
char sun_path[UNIX_PATH_MAX]; /* pathname */ socket文件名(含路径)
};
变化代码
2. 地址结构: sockaddr_in --> sockaddr_unstruct sockaddr_in srv_addr; --> struct sockaddr_un srv_adrr;srv_addr.sin_family = AF_INET; --> srv_addr.sun_family = AF_UNIX;
·srv_addr.sin_port = htons(8888); strcpy(srv_addr.sun_path, "srv.socket")srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); len = offsetof(struct sockaddr_un, sun_path) + strlen("srv.socket");bind(fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr)); --> bind(fd, (struct sockaddr *)&srv_addr, len);
- bind()函数调用成功,会创建一个 socket。因此为保证bind成功,通常我们在 bind之前, 可以使用 unlink(“srv.socket”);
4. 客户端不能依赖 “隐式绑定”。并且应该在通信建立过程中,创建且初始化2个地址结构:1) client_addr --> bind()2) server_addr --> connect();