1. 程式人生 > 其它 >Udp---模擬實現客戶端與伺服器通訊

Udp---模擬實現客戶端與伺服器通訊

技術標籤: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);//關閉套接字
}