Udp---模擬實現客戶端與伺服器通訊
阿新 • • 發佈:2020-12-21
技術標籤:udp
//客戶端模組
#include <iostream>
#include <cstdio>//stdio.h
#include <string>//std::string
#include <unistd.h>//close介面
#include <stdlib.h>//atoi介面
#include <netinet/in.h>//地址結構定義
#include <arpa/inet.h>//位元組序轉換介面
#include <sys/socket.h>//套接字介面
class UdpSocket
{
public :
UdpSocket():_sockfd(-1){
}
//1. 建立套接字
bool Socket() {
//socket(地址域, 套接字型別,協議型別);
//AF_INET-標識這是IPv4的通訊,並且提供的是資料報傳輸服務,使用的協議是UDP協議
_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (_sockfd < 0) {
perror ("socket error");
return false;
}
return true;
}
//2. 為套接字繫結地址資訊
bool Bind(const std::string &ip, uint32_t port) {
//1. 定義IPv4地址結構
struct sockaddr_in addr;
addr.sin_family = AF_INET;//地址域,用於向bind介面表明這是一個ipv4地址結構
addr.sin_port = htons(port);//網路位元組序的埠資訊
addr.sin_addr.s_addr = inet_addr(ip.c_str());//網路位元組序的IP地址資訊
//2. 繫結地址
socklen_t len = sizeof(struct sockaddr_in);
//bind(描述符, 統一地址結構sockaddr*, 地址資訊長度)
int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
if (ret < 0) {
perror("bind error");
return false;
}
return true;
}
//3. 傳送資料
bool Send(const std::string &data, const std::string &ip, uint16_t port) {
//sendto(描述符,資料,長度,選項, 對端地址,地址長度)
//1. 定義對端地址資訊的ipv4地址結構
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
//2. 向這個地址傳送資料
int ret;
socklen_t len = sizeof(struct sockaddr_in);
ret = sendto(_sockfd, data.c_str(), data.size(), 0, (struct sockaddr*)&addr, len);
if (ret < 0) {
perror("sendto error");
return false;
}
return true;
}
//4. 接收資料
bool Recv(std::string *buf, std::string *ip = NULL, uint16_t *port = NULL) {
//recvfrom(描述符,緩衝區,資料長度,選項,對端地址,地址長度)
struct sockaddr_in addr;//用於獲取傳送端地址資訊
socklen_t len = sizeof(struct sockaddr_in);//指定地址長度以及獲取實際地址長度
int ret;
char tmp[4096] = {0};//臨時用於存放資料的緩衝區
ret = recvfrom(_sockfd, tmp, 4096, 0, (struct sockaddr*)&addr, &len);
if (ret < 0) {
perror("recvfrom error");
return -1;
}
buf->assign(tmp, ret);//給buf申請ret大小的空間,從tmp中拷貝ret長度的資料進去
if (ip != NULL) {
*ip = inet_ntoa(addr.sin_addr);//將網路位元組序整數IP地址轉換為字串地址,返回
}
if (port != NULL) {
*port = ntohs(addr.sin_port);
}
return true;
}
//5. 關閉套接字
void Close() {
close(_sockfd);
_sockfd = -1;
return ;
}
private:
//貫穿全文的套接字描述符
int _sockfd;
};
#define CHECK_RET(q) if((q)==false){return -1;}
int main(int argc, char *argv[])
{
if (argc != 3) {
printf("em: ./udp_cli 192.168.122.132 9000\n");
return -1;
}
std::string ip_addr = argv[1];//服務端地址資訊
uint16_t port_addr = atoi(argv[2]);
UdpSocket sock;
CHECK_RET(sock.Socket());//建立套接字
//CHECK_RET(sock.Bind());//繫結地址資訊
while(1) {
//獲取一個標準輸入的資料,進行傳送
std::cout << "client say: ";
fflush(stdout);
std::string buf;
std::cin >> buf;// 獲取標準輸入的資料
sock.Send(buf, ip_addr, port_addr);//向指定的主機程序傳送buf資料
buf.clear();//清空buf緩衝區
sock.Recv(&buf);//因為本身客戶端就知道服務端的地址,因此不需要再獲取了
std::cout << "server say: " << buf << std::endl;
}
sock.Close();
return 0;
}
//服務端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h> //sockaddr結構體/ IPPROTO_UDP
#include <arpa/inet.h> //包含一些位元組序轉換的介面
#include <sys/socket.h>//套接字介面標頭檔案
int main(int argc, char *argv[])
{
//argc表示引數個數,通過argv向程式傳遞埠引數
if (argc != 3) {
printf("./udp_srv ip port em: ./udp_srv 192.168.122.132 9000\n");
return -1;
}
const char *ip_addr = argv[1];
uint16_t port_addr = atoi(argv[2]);
//socket(地址域, 套接字型別, 協議型別)
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) {
perror("socket error");
return -1;
}
//bind(套接字描述符, 地址結構, 地址長度);
//struct sockaddr_in ipv4地址結構
// struct in_addr{ uint32_t s_addr };
struct sockaddr_in addr;
addr.sin_family = AF_INET;
//htons-將兩個位元組的主機位元組序整數轉換為網路位元組序的整數
addr.sin_port = htons(port_addr);//注意千萬不要使用htonl
//inet_addr 將一個點分十進位制的字串IP地址轉換為網路位元組序的整數IP地址
addr.sin_addr.s_addr = inet_addr(ip_addr);
socklen_t len = sizeof(struct sockaddr_in);//獲取IPv4地址結構長度
int ret = bind(sockfd, (struct sockaddr*)&addr, len);
if (ret < 0) {
perror("bind error");
return -1;
}
while(1) {
char buf[1024] = {0};
struct sockaddr_in cliaddr;
socklen_t len = sizeof(struct sockaddr_in);
//recvfrom(描述符,緩衝區,長度,引數,客戶端地址資訊, 地址資訊長度)
//阻塞接收資料,將資料放入buf中,將傳送端的地址放入cliaddr中
int ret = recvfrom(sockfd, buf, 1023, 0, (struct sockaddr*)&cliaddr,&len);
if (ret < 0) {
perror("recfrom error");
close(sockfd);//關閉套接字
return -1;
}
printf("client say: %s\n", buf);
printf("server say:");
fflush(stdout);//讓使用者輸入資料,傳送給客戶端
memset(buf, 0x00, 1024); //清空buf中的資料
scanf("%s", buf);
//通過sockfd將buf中的資料傳送到cliaddr客戶端
ret =sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&cliaddr, len);
if (ret < 0) {
perror("sendto error");
close(sockfd);//關閉套接字
return -1;
}
}
close(sockfd);//關閉套接字
}