1. 程式人生 > >UDP協議實現聊天小程式

UDP協議實現聊天小程式

  今天我們用之前講解過的UDP協議來寫一個最基礎,最簡單的網路聊天程式。

//我們通過udp協議來實現一個簡單的網路聊天程式
//這是客戶端的實現
//過程:
//    1.建立套接字
//    2.繫結地址資訊
//    3.向服務端傳送資料
//    4.接受服務端傳送的資料
//    5.關閉socket

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

int main()
{
    int sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
    //socket函式第一個引數為地址型別,目前只支援apra型別的地址
    //第二個引數是套接字型別 如果是SOCK_DGRAM 面向資料報的套接字
    //如果是SOCK_STREAM則是面向資料流的套接字 不
    //第三個引數是用來指定具體協議,不想指定可以省略為0 預設資料報套接字為udp 資料流為tcp
    //這裡我們指定了IP協議族中的UDP協議。
    if(sockfd<0)
    {
        perror("socket error\n");
        return -1;
    }
    //繫結地址資訊,不過在客戶端的時候我們通常不繫結
    //因為繫結的話可能會因為一些特殊情況失敗,但是
    //客戶端傳送資料的時候使用哪個地址和埠都無所謂
    //只要資料能夠傳送成功就可以,所以客戶端時我們一般
    //不推薦繫結埠,這樣在傳送資料的時候,作業系統檢測
    //到socket還沒有繫結地址,就會自動給他繫結一個這樣
    //繫結方式不會出錯。
    //
    struct sockaddr_in cli_addr;
    //sockaddr_in 是一個結構體解決了sockaddr的缺陷
    //裡邊有四個成員變數分別是:
    //sin_family   儲存地址族
    //sin_port 16位TCP/UDP埠號
    //sin_addr 32位IP地址
    //sin_zeor[8]  我們不會使用到
    cli_addr.sin_family=AF_INET;
    cli_addr.sin_port=htons(9000);
    cli_addr.sin_addr.s_addr=inet_addr("192.168.76.130");
    socklen_t len=sizeof(struct sockaddr_in);
    while(1)
    {
        //3.傳送資料
        char buff[1024]={0};
        scanf("%s",buff);
        sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&cli_addr,len);
        //sendto 指向一指定的目的地傳送資料
        //第一個引數是我們套接字操作符
        //第二個引數是要傳送的資料
        //第三個引數傳送的資料長度
        //第四個一般為0,如果緩衝區沒有資料那麼阻塞等待
        //第五個指標指向目的套接字的地址
        //第六個引數是第五個指標指向的地址長度
        //
        //4.接受來自服務端的資料
        memset(buff,0x00,1024);//把緩衝區變成0以防剛剛的資料影響現在新接受的資料
        ssize_t r_len=recvfrom(sockfd,buff,1023,0,(struct sockaddr*)&cli_addr,&len);
        //recvfrom 是通過sock來接受資料的函式
        //引數和上邊的相同不過這裡第六個引數也是指標指向緩衝區的長度
        //如果成功返回的是接受的位元組數,失敗返回-1
         if(r_len<0)
         {
             perror("recvfrom error\n");
             return 0;
         }
         printf("server say:%s\n",buff);
    }
    close(sockfd);
    return 0;
}

這是客戶端的程式,服務端和客戶端沒有什麼區別,不過要注意一個是先發資料,一個是先收資料,不然兩個程式會同時阻塞卡住。

//這個是UDP協議聊天程式的服務端

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

int main()
{
    int sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
    if(sockfd<0)
    {
        perror("socket error\n");
        return -1;
    }
    struct sockaddr_in ser_addr;
    ser_addr.sin_family=AF_INET;
    ser_addr.sin_port=htons(9000);
    ser_addr.sin_addr.s_addr=inet_addr("192.168.76.130");
    socklen_t len=sizeof(struct sockaddr_in);
    int ret=bind(sockfd,(struct sockaddr*)&ser_addr,len);
    //bind 函式將所建立的套接字繫結到一個地址並且賦予一個埠號 
    //第一個引數是套接字描述符,
    //第二個引數為要繫結的地址資訊
    //第三個引數為地址資訊的長度。
    //返回-1代表繫結失敗
    if(ret<0)
    {
        perror("bind error\n");
        close(sockfd);
        return -1;
    }
    while(1)
    {
        //接受資料
        char buff[1024]={0};
        struct sockaddr_in cli_addr;
        len=sizeof(struct sockaddr_in);
        ssize_t rlen=recvfrom(sockfd,buff,1023,0,(struct sockaddr*)&cli_addr,&len);
        if(rlen<0)
        {
            perror("recvfrom error\n");
            close(sockfd);
            return -1;
        }
        printf("client:%s %d say:%s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buff);
        memset(buff,0x00,1024);
        scanf("%s",buff);
        sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&cli_addr,len);

    }
    close(sockfd);
    return -1;
}

一定是要弄明白哪個先收哪個先發, 不然可能會出現發了那邊收不到,只有另一邊也發的時候才能收到上一個對方傳送的資料。

這裡我們還在服務端輸出了,與他對話的客戶端的ip地址和埠號