linux--實現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表示的地址信息的長度

在這裏插入圖片描述
注意:

  1. UDP也是有發送緩衝區的,只是UDP協議的特點是面向數據報,他是整條信息直接發送的。
    所以說當數據從應用層到達傳輸層的過程,也就是到發送緩衝區中,數據會被直接打上UDP協議的頭部,然後直接提交到網絡層,中間的阻塞時間可以說很短暫
  2. 阻塞發送,是指當發送緩衝區中有數據的時候,這個階段如果再次調用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;
}

運行結果

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章