UDP協議的套接字
函數接口
發送數據接口
#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);
- sockdf:套接字的描述符
- buf:待發送的數據
- len:發送數據的長度
- flags:傳入0表示阻塞發送
- dest_addr:目標主機的地址信息,封裝在
struct sockaddr
結構體中 - addrlen:dest_addr表示的地址信息的長度
注意:
- UDP也是有發送緩衝區的,只是UDP協議的特點是面向數據報,他是整條信息直接發送的。
所以說當數據從應用層到達傳輸層的過程,也就是到發送緩衝區中,數據會被直接打上UDP協議的頭部,然後直接提交到網絡層,中間的阻塞時間可以說很短暫 - 阻塞發送,是指當發送緩衝區中有數據的時候,這個階段如果再次調用sendto接口,會讓數據發送的過程陷入阻塞等待。
直到上一條數據發送完畢,sendto接口纔可以將數據發送到緩衝區中
接收數據接口
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
- sockfd:套接字描述符
- buf :把從接收緩衝區中拿到數據放到buf數組中
- len:buf數組的最大長度,也就是最大可以結束多少數據,在末尾需要預留一個’
\0'
的位置 - flags:0表示阻塞接收數據
- src_addr:源主機的地址信息,標識着這條信息從哪一個主機的哪一個進程來
- addrlen:src_addr地址信息的長度,是一個輸入輸出型參數,傳的是長度的地址。
關閉套接字接口
#include <unistd.h>
int close(int fd);
因爲socket函數的返回值也是一個文件描述符,所以就可以使用close接口來關閉。
UDP通信小程序
實現的功能
服務端 (server):
- 創建套接字
- 綁定地址信息
- 接收數據
- 發送數據
- 關閉套接字
客戶端(client):
- 創建套接字
- 發送數據
- 接收數據
- 關閉套接字
流程
程序代碼
udpClass.hpp,將重複的部分集中到一個類中
#pragma once
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
using std::cin;
using std::cout;
using std::endl;
using std::string;
class udpClass
{
public:
//構造函數、
udpClass()
{
_sock = -1;
}
//socket 創建套接字
bool CreateSocket()
{
//int socket(int domain, int type, int protocol);
//選擇ipv4-->AF_INET 面向數據報--> SOCK_DGRAM UDP協議-->IPPROTO_UDP
_sock = socket(AF_INET, SOCK_DGRAM,IPPROTO_UDP);
if(_sock < 0)
{
//創建套接字失敗
perror("socket error ");
return false;
}
return true;
}
// bind 綁定Ip地址 端口信息
bool Bind(string& ip,uint16_t port)
{
//int bind(int sockfd, const struct sockaddr *addr,
// socklen_t addrlen);
struct sockaddr_in addr;
addr.sin_family = AF_INET;//按照ipv4的格式進行解析
addr.sin_port = htons(port);//小端字節序轉爲大端字節序,16位
//inet_addr--> 點分十進制的ip轉爲uint32格式, 將主機字節序轉爲網絡字節序
//ip.c_str --> string 類型轉爲 char* 類型
addr.sin_addr.s_addr = inet_addr(ip.c_str());
int ret = bind(_sock,(struct sockaddr*)&addr,sizeof(struct sockaddr_in));
if(ret < 0)
{
perror("bind error ");
return false;
}
return true;
}
//send 發送數據
bool Send(string& data, struct sockaddr_in* addr)
{
//ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
// const struct sockaddr *dest_addr, socklen_t addrlen);
int sendsize = sendto(_sock,data.c_str(),data.size(),0,
(struct sockaddr*)addr,sizeof(struct sockaddr_in));
if(sendsize < 0)
{
perror("send error ");
return false;
}
return true;
}
//recvfrom 接收數據
bool Recvfrom(string& buf,struct sockaddr_in* addr)
{
//ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
// struct sockaddr *src_addr, socklen_t *addrlen);
char data[1024] = {'\0'};
socklen_t addrlen = sizeof(struct sockaddr_in);
int recvsize = recvfrom(_sock,data,sizeof(data),0,
(struct sockaddr*)addr, &addrlen);
if(recvsize < 0)
{
perror("recvfrom error ");
return false;
}
//拷貝接收的數據i
//從data數組中截取前recvsize個字符
buf.assign(data,recvsize);
return true;
}
//close
void Close()
{
close(_sock);
_sock = -1;
}
private:
int _sock;//套接字描述符
};
- 注意:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
這種情況下,我們在用sizeof
計算sockaddr_in
結構體的大小的時候,不能直接對變量名使用sizeof,因爲原變量的類型是指針類型,他的大小在32位機器下一直是4個字節,在64位機器下是8個字節。所以需要直接對原變量的結構體類型進行sizeof,這樣才能求出準確的大小。
server.cpp ,服務端的程序
#include "udpClass.hpp"
int main(int argc, char* argv[])
{
//所需要的三個參數 ./server ip port
if(argc != 3)
{
cout<<"所輸入的參數不正確,應該爲:[./server] [ip] [port]"<<endl;
return 0;
}
string ip = argv[1];
uint16_t port = atoi(argv[2]);
udpClass udp;
//創建套接字
if(!udp.CreateSocket())
return 0;
//綁定ip和端口信息
if(!udp.Bind(ip,port))
return 0;
while(1)
{
//接收數據
string buf;
struct sockaddr_in cliaddr;//對端地址信息
udp.Recvfrom(buf,&cliaddr);
cout<<"client say : "<<buf<<endl;
//發送數據
cout<<"server respond : ";
fflush(stdout);//刷新緩衝區
cin>>buf;
udp.Send(buf,&cliaddr);
}
udp.Close();
return 0;
}
client.cpp ,客戶端程序
#include "udpClass.hpp"
int main(int argc, char* argv[])
{
if(argc != 3)
{
//ip port 是服務端的ip port
cout<<"參數錯誤 [./client] [ip] [port]"<<endl;
return 0;
}
string ser_ip = argv[1];
uint16_t ser_port = atoi(argv[2]);
udpClass udp;
//創建套接字
if(!udp.CreateSocket())
return 0;
//服務端的地址信息
struct sockaddr_in ser_addr;
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(ser_port);
ser_addr.sin_addr.s_addr = inet_addr(ser_ip.c_str());
while(1)
{
//發送數據
cout<<"client say : ";
fflush(stdout);
string buf;
cin>>buf;
udp.Send(buf,&ser_addr);
//接收數據
struct sockaddr_in dataaddr;
udp.Recvfrom(buf,&dataaddr);
cout<<"server say : "<<buf<<endl;
}
udp.Close();
return 0;
}