太原网站建设tygytc网络营销应用方式
提要
UDP的缺点:
(1)丢得少不找回,可以设置阈值
(2)数据段顺序不能保障
UDP的优点:
没有繁琐的连接过程
TCP和UDP:
(1)TCP通常比UDP慢点:
1.TCP收发数据前后要进行连接和关闭清理。
2.收发数据量越小,TCP效率就越低。
(2)TCP能保障数据的完整性。
(3)TCP中套接字是一对一关系,服务端为了和多个客户端通信,要建立多个用于通信的套接字
(4)UDP中,客户端和服务端都只需要一个套接字
实验
UDP通信的程序中
先 完成对套接字的地址分配工作
然后 调用sendto传输数据,调用sendto函数时自动分配IP和端口号
代码步骤
服务端:
1.创建套接字描述符、udp套接字;
2.创建并设置地址结构体,用bind函数把结构体绑定到套接字;
3.recvfrom函数用于接收;sendto函数用于发送
客户端:
1.创建套接字描述符、创建UDP套接字;
2.创建地址结构体,结构体要设置服务器的IP和端口号。
3.sendto用于发送;recvfrom用于接收
sendto函数和recvfrom函数
#include <sys/types.h>
#include <sys/socket.h>ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
recvfrom()
sockfd:套接字文件描述符
buf:指向存储接收数据的缓冲区的指针
len:缓冲区的大小
flags:通常设置为0
src_addr:指向存储发送方地址的sockaddr结构的指针
addrlen:指向存储src_addr长度的变量的指针
返回值:成功时返回接收到的字节数,失败返回-1。
flags
MSG_PEEK:查看数据但不从队列中移除。
MSG_WAITALL:等待接收完整的消息(仅用于TCP)。
MSG_DONTWAIT:非阻塞模式。
sendto
sockfd:套接字文件描述符
buf:指向包含要发送数据的缓冲区的指针
len:要发送的数据的长度
flags:通常设置为0
dest_addr:指向包含目标地址的sockaddr结构的指针
addrlen:dest_addr的长度
返回值:成功时返回发送的字节数,失败返回-1
flags
MSG_CONFIRM:请求对方确认数据已接收
MSG_DONTROUTE:绕过路由表,直接发送数据
MSG_DONTWAIT:非阻塞模式
代码示例
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h> //struct sockaddr_in,htons(),htonl(),INADDR_ANY
#include <stdlib.h>//atoi()
#include <sys/wait.h>//close()
#include <arpa/inet.h>//inet_addr()
int udp_server(int argc, char* argv[]) {int ser_sock = -1;//服务器套接字描述符char message[512] = "";struct sockaddr_in servaddr {};//服务器地址结构体//检查命令行参数数量if (argc != 2) {printf("usage:%d <port>\n", argv[0]);printf("error:argement is error\n");return -1;}//创建udp套接字ser_sock = socket(PF_INET, SOCK_DGRAM, 0);if (ser_sock == -1) {printf("error:create socket failed\n");return -1;}//设置服务器结构体servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//监听所有可用端口servaddr.sin_port = htons((short)atoi(argv[1]));//转换端口号为网络字节序//绑定套接字到指定地址和端口if (bind(ser_sock, (struct sockaddr*) & servaddr, sizeof(servaddr)) == -1) {printf("error:bind failed");close(ser_sock);return -1;}struct sockaddr_in clientaddr;//socklen_t clientlen{};//循环接收并回显消息10次for (int i = 0; i < 10; i++) {clientlen = sizeof(clientaddr);ssize_t len = recvfrom(ser_sock, message, sizeof(message), 0, (struct sockaddr*)&clientaddr, &clientlen);sendto(ser_sock, message, len, 0, (struct sockaddr*)&clientaddr, clientlen);}close(ser_sock);return 0;
}
#include<string.h>//strcmp()
int udp_client(int argc, char* argv[])
{int client_sock;//客户端套接字描述符struct sockaddr_in serv_addr;//地址结构体,存的是服务端的地址socklen_t serv_len = sizeof(serv_addr);char message[512] = "";if (argc != 3) {printf("usage:%s ip port\n", argv[0]);printf("error:argement error!");return -1;}//创建UDP套接字client_sock = socket(PF_INET, SOCK_DGRAM, 0);if (client_sock == -1) {printf("error:socket create failed!");return -1;}//设置地址结构体serv_addr.sin_family = AF_INET;//地址族IPv4serv_addr.sin_addr.s_addr = inet_addr(argv[1]);//服务器IP地址serv_addr.sin_port = htons((short)atoi(argv[2]));//服务器端口号//循环发送和接收消息,直到用户输入‘q’或‘Q’while (1) {printf("input message(q to Quit):");scanf("%s", message);if ((strcmp(message, "q") == 0) || (strcmp(message, "Q") == 0)) {break;}ssize_t len = sendto(client_sock, message, strlen(message), 0, (sockaddr*)&serv_addr, serv_len);memset(message, 0, sizeof(message));recvfrom(client_sock, message, sizeof(message), 0, (sockaddr*)&serv_addr, &serv_len);printf("recv:%s\n", message);}close(client_sock);return 0;
}void test(char* argv0)
{if (fork() > 0) {int argc = 3;char* argv[] = {argv0,(char*)"127.0.0.1",//const char[]类型转换为char*类型,避免被警告(char*)"9999"};udp_client(argc, argv);int status = 0;wait(&status);//等待子进程结束}else {int argc = 2;char* argv[] = {argv0,(char*)"9999"};udp_server(argc,argv);}
}int main(int argc,char* argv[])
{test(argv[0]);return 0;
}