数据报套接字用于实现UDP通信
1.socket()
int socket(int domain,int type,int protocol); // 失败返回-1
domain - 协议族:(1)AF_UNIX,AF_LOCAL 本地通讯(2)AF_INET IPV4 (3) AF_INET6 IPV6
type - 实现方式: (1)SOCK_STREAM 流式 (2)SOCK_DGRAM 报式
protocol - 协议:一般为0
2.htonl / htons / ntohl / ntohs
字节序:
字节序与网络字节序转换
uint32_t htonl(uint32_t hostlong); //32位主机字节序转换到网络字节序
uint16_t htons(uint16_t hostshort); //16位主机字节序转换到网络字节序
uint32_t ntohl(uint32_t netlong); //32位网络字节序转换到主机字节序
uint16_t ntons(uint16_t netshort); // 16位网络字节序转换到主机字节序
大端的网络字节序和主机字节序一致,不需要使用以上四个函数。如果 htonl(1)==1则为大端模式
3.bind()将套接字与地址关联
int sockfd = socked(int domain,int type,int protocol)
int bind(sockfd, const struct sockaddr *addr, socklen_t addrlen);
addlen:协议地址addr空间大小
addr - 协议地址,依赖于domain的地址信息
以AF_INET为例,它的协议地址类型为
struct sockaddr_in{
//协议族:AF_INET
sa_family_t sin_family;
//端口
in_port_t sin_port;
//IP地址,不能是点分式192.168.20.22,需要是大整数,因此要格式转换
struct in_addr sin_addr;
};
struct in_addr{
//大整数形IP地址
uint32_t s_addr;
};
4.IP类型转换:inet_pton() / inet_ntop()
(1)inet_pton():点分式转大整数形
int inet_pton(int af,const char *src,void *dst)
af:协议族,只能是AF_INET或AF_INET6,即IPv4或IPv6
src:点分式的IP地址
dst:转换之后的存储空间
成功返回1,失败返回-1
(2)inet_ntop():大整数形转点分式
size:dst指向的空间大小
成功返回dst首地址,失败返回NULL
5.接收recvfrom()
ssize_t recvfrom(sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addlen);
buf:接收信息存储地址
len:接受信息存储空间大小
flags:特殊要求,一般为0
src_addr:发送端地址
addrlen:发送端地址空间大小
成功返回接收到的字节数,失败返回-1
6.发送sendto()
ssize_t sendto(sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
buf:发送数据信息存储地址
len:发送数据信息存储空间
flags:特殊要求
dest_addr:接收端的地址
addlen:接收端地址空间大小
成功返回发送的字节数,失败返回-1
7.本地回环测试
(1)proto.h
#ifndef PROTO_H__
#define PROTO_H__
#include <stdint.h>
#define RCVER_PORT "2989"
#define NAMESIZE 13
struct msg_st
{
uint8_t name[NAMESIZE];
uint32_t math;
uint32_t chinese;
}__attribute__((packed));
#endif
(2)rcver.c
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include "proto.h"
#define IPSTRSIZE 128
int main()
{
int sd;
struct sockaddr_in laddr,raddr;
struct msg_st rbuf;
socklen_t raddr_len;
char ipstr[IPSTRSIZE];
sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
if(sd < 0)
{
perror("socket()");
exit(1);
}
laddr.sin_family = AF_INET;
laddr.sin_port = htons(atoi(RCVER_PORT));
inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
if(bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
{
perror("bind()");
exit(1);
}
raddr_len = sizeof(raddr);
while(1)
{
if(recvfrom(sd,&rbuf,sizeof(rbuf),0,(void *)&raddr,&raddr_len) < 0)
{
perror("recvfrom()");
exit(1);
}
inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
printf("-----MESSAGE FROM:%s:%d-------\n",ipstr,ntohs(raddr.sin_port));
printf("NAME:%s\n",rbuf.name);
printf("MATH:%d\n",ntohl(rbuf.math));
printf("CHINESE:%d\n",ntohl(rbuf.chinese));
}
close(sd);
exit(0);
}
(3)snder.c
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include "proto.h"
#define IPSTRSIZE 128
int main(int argc,char *argv[])
{
int sd;
struct sockaddr_in raddr;
struct msg_st sbuf;
if(argc < 2)
{
fprintf(stderr,"Usage...\n");
exit(1);
}
sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
if(sd < 0)
{
perror("socket()");
exit(1);
}
//bind();
memset(&sbuf,'\0' ,sizeof(sbuf));
strcpy(sbuf.name,"Alan");
sbuf.math = htonl(rand()%100);
sbuf.chinese = htonl(rand()%100);
raddr.sin_family = AF_INET;
raddr.sin_port = htons(atoi(RCVER_PORT));
inet_pton(AF_INET,argv[1],&raddr.sin_addr);
if(sendto(sd,&sbuf,sizeof(sbuf),0,(void *)&raddr,sizeof(raddr)) < 0)
{
perror("sendto()");
exit(1);
}
puts("OK");
close(sd);
exit(0);
}
gcc rcver.c proto.h -o rcver 编译
./rcver 运行
打开新的终端,输入netstat -anu,0.0.0.0:2989出现
在新的终端继续输入:
gcc snder.c proto.h -o snder 编译
./snder 127.0.0.1 运行
在rcver的终端就可以显示了