UDP编程
socket常见API
//创建socket文件描述符(TCP/UDP,客户端 + 服务器)
int socket(int domain,int type,int protocol)
//绑定端口号(TCP/UDP,服务器)
int bind(int socket , const struct sockaddr *address,
socklen_t address_len)
//开始监听socket (TCP,服务器)
int listen (int socket , int backlog)
//接收请求(TCP,服务器)
int accept(int socket,struct sockaddr* address,
socklen_t* address_len);
//建立连接(TCP,客户端)
int connect (int sockfd,const struct sockaddr *addr,
socklen_t addrlen);
这里我们通过完成一个回显式服务器来完成UDP传输的过程
sockaddr结构:
头文件:#include <sys/socket.h>
struct sockaddr {
sa_family_t sin_family;//地址族
char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息
};
sockaddr_in结构:
//头文件:
#include<netinet/in.h>
#include <arpa/inet.h>
struct sockaddr_in {
short int sin_family; /* Address family */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
struct in_addr {
unsigned long s_addr;
};
typedef struct in_addr {
union {
struct{
unsigned char s_b1,
s_b2,
s_b3,
s_b4;
} S_un_b;
struct {
unsigned short s_w1,
s_w2;
} S_un_w;
unsigned long S_addr;
} S_un;
} IN_ADDR;
sin_family -> 指代协议族,在socket编程中只能是AF_INET
sin_port -> 存储端口号(使用网络字节顺序)
sin_addr -> 存储IP地址,使用in_addr这个数据结构
sin_zero -> 是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
s_addr -> 按照网络字节顺序存储IP地址
实例:
服务端:
//UDP版本
#include<sys/socket.h>
#include<sys/types.h>
#include<error.h>
#include<cstdio>
#include<cstring>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
int main()
{
//1.创建一个socket
//AF_INET 是 一个宏,表示使用ipv4协议
//SOCK_DGRAM表示使用UDP协议
//int socket(int domain, int type, int protocol);
int sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock < 0) { //创建失败
perror("socket");
return 1;
}
//2.给当前的socket绑定上一个ip + 端口号
sockaddr_in addr; //这是一个结构体
addr.sin_family = AF_INET;
//ip地址也是一个整数,也需要转成网络字节序,但是inet_addr帮我们转好了
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
//这个是可以接收的IP地址,0代表所有的IP地址
//主机字节序的数字转成网络字节序
addr.sin_port = htons(9090);
//9090端口无特殊含义,比较不常用
int ret = bind(sock,(sockaddr*)&addr,sizeof(addr));
if(ret < 0){
perror("bind");
return 1;
}
printf("sever start ok!\n");
//3.处理服务器收到的请求
while(1){
//a)读取客户端的请求
//面向数据报的函数接口
sockaddr_in peer;
socklen_t len = sizeof(peer); //输入输出参数
char buf[1024] = {0}; //缓冲区
ssize_t n=recvfrom(sock,buf,sizeof(buf)-1,0,
(sockaddr*)&peer,&len);
if( n < 0)
{
perror("recvfrom");
continue;
}
buf[n] = '\0';
//b) 根据请求计算响应
//此处响应和请求相同,只需要回复发来的信息即可
//c) 把响应写回客户端
n = sendto(sock,buf,strlen(buf),0,(sockaddr*)&peer,len);
if(n < 0)
{
perror("sendto");
continue;
}
printf("[%s:%d] buf: %s\n",inet_ntoa(peer.sin_addr),
ntohs(peer.sin_port),buf);
}
close(sock);
return 0;
}
客户端:
#include<cstdio>
#include<cstring>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
// .client 127.0.0.1
int main(int argc,char* argv[])
{
//1.先创建一个socket
int sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock < 0)
{
perror("socket");
return 1;
}
//客户端一般不需要bind
//bind意味着和某个具体的端口号连接在一起
//如果没有bind,操作系统会随机分配一个端口号
//如果是服务器程序不去bind,就会导致每次启动服务的端口都会变化
//客户端就没法连接了
//准备好服务器的sockaddr_in 结构
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
server_addr.sin_port = htons(9090);
//4.客户端直接发送数据即可
while(1){
char buf[1024] = {0};
printf("请输入一段内容:");
fflush(stdout);
scanf("%s",buf);
sendto(sock,buf,strlen(buf),0,
(sockaddr*)&server_addr,sizeof(server_addr));
//从服务器接收一下返回结果
char buf_output[1024] = {0};
//后两个参数填NULL表示不关注对端的地址
recvfrom(sock,buf_output,sizeof(buf_output),0,NULL,NULL);
printf("server resp: %s\n",buf_output);
}
return 0;
}