网络编程二(UDP)
UDP可能出现缓冲区被填满,再次接收数据会出现丢包的情况.UDP没有滑动窗口机制,所以靠以下方法解决:
1>在服务器应用层设计流量控制,控制发送数据的速度;
2>使用setsockopt()函数修改缓冲区大小.
#include<sys/socket.h>
//原型:int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t optlen);
int n 220*1024
setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&n,sizeof(n));
1UDP实现应用场景
- 控制函数
/***********************socket函数********************************
*函数1:socket()
*头文件:#include <sys/types.h> #include <sys/socket.h>
*格式:int socket(int domain, int type, int protocol);
*参数:int domain:AF_UNIX(本地套接字);AF_INET(ipv4);AF_INET6(ipv6)
*参数:int type(传输协议重点两个值):SOCK_STREAM(最常用)(基于字节流TCP-可靠);SOCK_DGRAM(无连接,固定长度,基于UDP协议-不可靠)
*参数:int protocol:一般传0;指定默认协议(及第二个参数的默认协议,TCP\UDP)
*作用:创建套接字
*返回值: 成功-fd(文件描述符);失败--1;
********************************************************
*函数2:bind()
*头文件:#include <sys/types.h> #include <sys/socket.h>
*格式: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
*参数:int sockfd:socket()函数返回的文件描述符;
*参数:const struct sockaddr *addr:创建socket数据结构,指定IP地址和端口号(注意)
struct sockaddr_in {
sa_family_t sin_family; //address family: AF_INET
in_port_t sin_port; //port in network byte order
struct in_addr sin_addr; //internet address
};
/* Internet address.
struct in_addr {
uint32_t s_addr; //address in network byte order
};
eg:struct sockaddr_in addr;
addr.sin_family=AF_INET\AF_INET6;
addr.sin_port=htons()/ntops() // 注意端口号需要调用函数转化字节序
addr.sin_addr.s_addr=htons()/ntops() /inet_ntop()/inet_pton() //注意IP也需要调用函数转化字节序
*参数:socklen_t addrlen:传入sizeof(addr)
*作用:将socket()函数返回的fd与addr(IP和端口号)绑定在一起,进而使用fd监听IP和端口号;
*返回值: 成功-0;失败--1;
********************************************************
*函数3:
*头文件: #include <sys/types.h> #include <sys/socket.h>
*格式: ussize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
*参数:int sockfd:socket文件描述符
*参数:void *buf:接收数据的buffer
*参数:size_t len:buffer大小
*参数:int flags:标志位,0-相当于read()函数
*参数:struct sockaddr *src_addr:传出参数,存储client信息的,sendto()函数需要使用
*参数:socklen_t *addrlen:传入传出参数,client_addr_len
*作用:从socket接收数据
*返回值:These calls return the number of bytes received, or -1 if an error occurred. In the event of an error, errno is set to indicate the error.
********************************************************
*函数4:sendto()
*头文件: #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);
*参数:int sockfd:socket文件描述符
*参数:void *buf:发送数据的buffer
*参数:size_t len:buffer大小
*参数:int flags:标志位,0-相当于read()函数
*参数: const struct sockaddr *dest_addr:传入参数,传入client信息的(才知道信息发给谁-所以不能传空)
*参数:socklen_t addrlen:第三个参数结构体的大小
*作用:向socket发送数据
*返回值: On success, these calls return the number of bytes sent. On error, -1 is returned, and errno is set appropriately.
********************************************************
*函数5:htonl()\htons()\ntohl()\ntohs
*头文件:#include <arpa/inet.h>
*格式: uint32_t htonl(uint32_t hostlong);\\htonl:h-host(主机);to;n-network(网络);l-long(4字节,对应的IP为4字节);s-short int (2字节,对应端口号为2字节)
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
*作用:主机字节序\网络字节序转换
********************************************************
*函数6:inet_pton() \inet_ntop()(更方便)
*头文件:#include <arpa/inet.h>
*格式: int inet_pton(int af, const char *src, void *dst);//int af(指定ipv4还是ipv6):AF_INET或则AF_INET6; const char *src(传入参数,ip地址字符串的首地址);void *dst:传出参数,转换为网络字节序
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);int af(指定ipv4还是ipv6):AF_INET或则AF_INET6;const char *src(传入参数,传入ip网络字节序);char *dst:(传出参数,传出主机字节序的ip字符串);socklen_t size:(第三个参数的长度)
*作用:inet_pton()-主机字节序(点分十进制)to网络字节序;inet_ntop:相反
*返回值:On success, inet_ntop() returns a non-null pointer to dst. NULL is returned if there was an error, with errno set to indicate the error.
*****************************socket函数**************************/
/*注意:1.定义的时候要定义为sockaddr_in,在做形参的时候要转化为(struct sockaddr *)类型,涉及的函数有bind()\accept()\connect();因为sockaddr_in是后来改的名字;函数不认
2.ipv6的数据结构又不一样
3.eg:
struct sockaddr_in addr;
addr.sin_family=AF_INET\AF_INET6;
addr.sin_port=htons()/ntops() // 注意端口号需要调用函数转化字节序
addr.sin_addr.s_addr=htons()/ntops() /inet_ntop()/inet_pton() //注意IP也需要调用函数转化字节序*/
1>C/S-UDP模型
- server.c
#include<stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include<unistd.h>
#include<stdlib.h>
#include <errno.h>
#include<string.h>
#include <sys/types.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080
int main(int argc, char *argv[])
{
int n; //recvfrom()函数返回值
int i;
char buffer[1024]; //存储传输数据
char buf[1024];//存放ip的,协议用的32位存放(数值型 unsigned int),而主机是存放的字符串类型,所以可以尽量开大一点.
//1.创建socket-ipv4,默认报式协议UDP
int fd=socket(AF_INET, SOCK_DGRAM, 0);
if(fd==-1)
{
perror("socke error");
exit(1);
}
//2.绑定端口
struct sockaddr_in server_addr,client_addr;
socklen_t client_addr_len=sizeof(client_addr);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(SERVER_PORT);
inet_pton(AF_INET,SERVER_IP,&server_addr.sin_addr.s_addr);
int ret1=bind(fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
if(ret1==-1)
{
perror("bind error");
exit(1);
}
printf("Waiting connect....\n");
while(1)
{
//3.接收数据
n=recvfrom(fd,buffer,sizeof(buffer),0,(struct sockaddr *)&client_addr,&client_addr_len); //会阻塞读
if(n==-1)
{
perror("recvfrom error:");
exit(1);
}
//4.打印客服端信息
printf("connect success,client IP is %s,port is %d.\n",inet_ntop(AF_INET,&client_addr