1.端口和网络字节序
端口
在主机中表示一个进程,uint16_t --> 0-65535(0-1023特定)
每条数据当中都包括 源端口+目的端口
一个端口只能被一个进程所占用
一个进程可以占用多个端口
网络当中数据的五元组:源IP+源端口+目的IP+目的端口+协议
网络字节序
字节序:CPU对内存当中的数据进行存取的顺序
大端字节序:低地址存高位
小端字节序:低地址存地位
主机字节序:当前计算的字节序
网络字节序:大端字节序
转换接口
uint32_t htonl(uint32_t hostlong):将主机字节序的4个字节的数据转化成网络字节序
uint16_t htons(uint16_t hostshort); 将主机字节序的2个字节的数据转化成网络字节序
uint32_t ntohl(uint32_t netlong); 将网络字节序的4个字节的数据转换成主机字节序
uint16_t ntohs(uint16_t netshort)将网络字节序的2个字节的数据转换成主机字节序
2.TCP和UDP
TCP特点:
传输层协议、有连接、可靠传输、面向字节流
UDP特点:
传输层协议、无连接、不可靠传输、面向数据报
3.UDP实现
3.1 UDP实现流程
创建套接字,就是在内核中创建了一个struct socket{…},在这个结构体中有两个缓冲区,一个是接收缓冲取,一个是发送缓冲区。操作系统内核的职责是将网卡收到的数据,进行端口辨认之后,拷贝到不同的socket的缓冲区,程序员调用的操作系统定义的接口的时候(sendto,recvfrom),和socket缓冲区进行交互。
3.2 UDP接口
1.创建套接字
int socket(int domain,int type,int protocol);
domain: 地址域信息,网络层
参数:
AF_INET: ipv4
AF_INET6: ipv6
type:套接字类型
参数:
SOCK_STREAM: 流式套接字,默认传输层的协议是tcp,不支持udp
SOCK_DGRAM: 数据报套接字,默认的传输层的协议是udp,不支持tcp
protocol:协议类型
参数
0:使用套接字的默认协议
TCP: IPPROTO_TCP:6
UDP: IPPROTO_UDP:17
返回值:套接字的操作句柄,套接字描述符
2. 绑定地址信息
int bind(int sockfd,const struct sockaddr* addr,socklen_t len)
sockfd: 套接字操作句柄
addr:
sockaddr:
地址类型,2个字节。
地址信息,14个字节char sa_data[14]。
ipv4:
地址类型,2字节。
uint16_t port, 2个字节
uint32_t ip, 4个字节
填充信息,8个字节
len:地址信息的长度
返回值成功返回0,失败返回-1
3. 发送数据
ssize_t sendto(int sockfd,const void *buf,size_t len,int flags,const struct sockaddr* dest_addr,socklen_t addrlen)
sockfd:套接字操作句柄
buf:要发送什么样的数据
len:要发送数据的长度
flags:一般设置为0,阻塞发送
dest_addr:目标地址信息
addrlen:地址信息的长度
4.接收数据
ssize_t recvfrom(int sockfd,void* buf,size_t len,int flags,struct sockaddr* src_addr,socklen_t* addrlen)
sockfd:套接字的操作句柄
buf:需要将接收到的数据存放到那里去
len’:接收数据buf的最大长度
flags:0,阻塞接受
src_addr:发送端地址信息
addrlen:地址信息长度
4. 关闭套接字
close(int sockfd)
3.3 代码实现
封装udp库,
//udpsvr.hpp
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<iostream>
#include<string>
class UdpSvr
{
public:
UdpSvr()
{
Sock_ = -1;
}
~UdpSvr()
{
}
bool CreateSock()
{
Sock_ = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(Sock_ < 0)
{
perror("socket");
return false;
}
return true;
}
bool Bind(std::string& ip,uint16_t port)
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
int ret = bind(Sock_,(struct sockaddr*)&addr,sizeof(addr));
if(ret < 0)
{
perror("bind");
return false;
}
return true;
}
bool Send(std::string&buf,struct sockaddr_in* destaddr)
{
int SendSize = sendto(Sock_,buf.c_str(),buf.size(),0,(struct sockaddr*)destaddr,sizeof(struct sockaddr_in));
if(SendSize < 0)
{
perror("sendto");
return false;
}
return true;
}
bool Recv(std::string& buf,struct sockaddr_in* srcaddr)
{
char tmp[1024] = {
0};
socklen_t socklen = sizeof(struct sockaddr_in);
int RecvSize = recvfrom(Sock_,tmp,sizeof(tmp) - 1,0,(struct sockaddr*)srcaddr,&socklen);
if(RecvSize < 0)
{
perror("Recv");
return false;
}
buf.assign(tmp,RecvSize);
return true;
}
void Close()
{
close(Sock_);
Sock_ = -1;
}
private:
int Sock_;
};
服务端
//svr.cpp
#include"udpsvr.hpp"
int main(int argc,char* argv[])
{
if(argc != 3)
{
printf("./svr [ip] [port]\n");
return 0;
}
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
UdpSvr us;
if(!us.CreateSock())
{
return 0;
}
if(!us.Bind(ip,port))
{