【Linux】之 UDP服務器與客戶端

學習了socket套接字之後,我試着根據相關接口搭載了一個自己的小型UDP服務器。在這裏做一下總結,記錄搭載過程。

一、什麼是socket(套接字)?

    所謂套接字,即是指在建立鏈接過程中的“套接”行爲。想實現兩端的互聯,就首先要唯一的標識出來對方,套接字的作用就是唯一的確定一臺主機上的一個進程。如果客戶端想要對服務器“套接”,就需要知道知道服務器的ip地址,當然,這隻能標識服務器的主機;還需要知道負責與你“套接”的進程,即是端口號(port)。只有先這樣“套接”之後,才具備了互聯,傳輸的前提。

PS:爲什麼不直接用進程ID標識進程呢?

這裏我是這麼理解的,進程ID是由操作系統內核分配管理的,我們沒通信之前,我怎麼知道你那的進程ID是什麼?所以我們約定好一個端口來標識我們負責套接的進程,這樣就很方便了。

二、UDP通信的特點

特點:無鏈接,面向數據報(安全性不高,實時性高的場景:視頻通話)傳輸速度快,無粘包。

缺點:不可靠

UDP建立連接的幾個步驟:

服務器端:                                                     客戶端:

1.創建套接字 socket                                       1.創建套接字

2.爲套接字綁定地址信息 bind                         2.綁定地址 (不推薦主動綁定,有操作系統自動完成,反正我不做這一步)

3.接受/發送數據 recvfrom sendto                   3.發送或接收數據

4.關閉套接字 close                                         4.關閉套接字

三、socket常用的API

這些都是大佬們給我們封裝好的,再不會用就說不過去了。

int socket(int domain, int type, int protocol);
    //domain:地址域	AF_INET代表IPV4網絡協議
    //type:套接字類型
    //	SOCK_STREAM	流式套接字	默認tcp
    //	SOCK_DGRAM	數據報套接字	默認udp
    //protocol:協議	IPPROTO_UDP	IPPROTO_TCP
    //返回值:套接字描述符(非負整數) 失敗:-1

int bind(int sockfd, struct sockaddr *addr,socklen_t addrlen);
    //  sockfd: 套接字描述符
    //  addr:  地址信息
    //      使用sockaddr_in定義,使用時進行類型強轉
    //  addrlen:   地址信息長度
    //  返回值:0       失敗:-1
    struct sockaddr_in addr;//綁定地址信息
    addr.sin_family  = AF_INET;//地址域h使用ipv4
    //uint16_t htons(uint16_t hostshort);
    //將uint16_t類型的數據從主機字節序轉換爲網絡字節序
    addr.sin_port = htons(9000);
    //uint32_t htonl(uint32_t hostlong);
    //將uint32_t類型的數據從主機字節序轉換爲網絡字節序
    //in_addr_t inet_addr(const char *cp);
    //將點分十進制字符串ip地址轉換爲網絡字節序ip地址
    addr.sin_addr.s_addr = inet_addr("192.168.122.135");
    //addr.sin_addr.s_addr = htonl(0xc0a87a87);
	socklen_t len = sizeof(struct sockaddr_in);
	//上面做都得都是把這 struct sockaddr_in 這個結構體的信息都填進去。

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
    //  sockfd: 套接字描述符---確定從哪裏獲取數據
    //  buf:   用於接收數據
    //  len:   接收的數據長度
    //  flags: 默認賦0---阻塞接收(沒數據就一直等待)
    //  src_addr:  數據從哪裏來,對端地址信息
    //  addrlen:   地址信息長度(輸入輸出複合參數)
    //  返回值:實際接收的數據長度      失敗:-1
注意:這裏有個坑!!! 最後一個地址信息長度,需要取地址。凡是接收類的都需要對地址信息取地址!! 如下 &len
	recvfrom(sockfd, buf, 1023, 0, (struct sockaddr*)&cli_addr, &len);

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,struct sockaddr *dest_addr, socklen_t addrlen);
    //  dest_addr:  目的端地址信息
    //  addrlen:   地址信息長度

int close(sockfd);

這裏有很重要的一個結構體,用來存放端口號,ip地址,地址域。模型如下

下面貼上我寫好的代碼,有一些坑會在代碼中註釋。

/*
*一個基於udp協議的網絡通信服務端
*/

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

int main()
{
    //創建套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sockfd < 0) {
        perror("socket error");
        return -1;
    }
    //把需要的信息都填到sockaddr_in這個結構體裏,再使用綁定接口
    struct sockaddr_in addr;//綁定地址信息
    addr.sin_family  = AF_INET;//地址域h使用ipv4
    addr.sin_port = htons(9420);
    addr.sin_addr.s_addr = inet_addr("172.20.10.3");
    socklen_t len = sizeof(struct sockaddr_in);
    int ret;
    //這個地方要強轉爲(struct sockaddr*)類型
    ret = bind(sockfd, (struct sockaddr*)&addr, len);
    if (ret < 0) {
        perror("bind error\n");
        return -1;
    }
    while(1) {
        //3. 接收數據
        char buff[1024] = {0};
        struct sockaddr_in cli_addr;
        recvfrom(sockfd, buff, 1023, 0,
                 (struct sockaddr*)&cli_addr, &len);//len要取地址
        printf("client say: %s\n", buff);
        //4. 發送數據
        memset(buff, 0x00, 1024);
        scanf("%s", buff);
        sendto(sockfd, buff, strlen(buff), 0, 
                (struct sockaddr*)&cli_addr,len);
    }
    //5. 關閉套接字
    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>

int main()
{
	int sockfd = 0;
	sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(sockfd < 0)
	{
		printf("cli socket create failed\n");
		return -1;
	}
	struct sockaddr_in ser_addr;
	ser_addr.sin_family = AF_INET;
	ser_addr.sin_port = htons(9420);
	ser_addr.sin_addr.s_addr = inet_addr("172.20.10.3");
	socklen_t addrlen = sizeof(struct sockaddr_in);
	//被坑了一次才知道,客戶端不要綁定,一綁定就出錯
    //int ret = 0;
	//ret = bind(sockfd, (struct sockaddr*)&ser_addr, addrlen);
	//if(ret < 0)
	//{
	//	printf("cli bind failed\n");
	//	return -1;
	//}
	while(1)
	{
		char buf[1024] = {0};
		scanf("%s",buf);
		sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&ser_addr, addrlen);
		memset(buf, 0x00, 1024);
		
		char buf2[1024] = {0};
		recvfrom(sockfd, buf2, 1023, 0, (struct sockaddr*)&ser_addr, &addrlen);
		printf("ser:%s\n",buf2);
	}
	close(sockfd);
	return 0;

}

我這個UDP服務器涉及的知識比較簡單淺顯,知識把基本的接口用了一遍。但是在後面的TCP服務器有一些複雜的地方了。

 

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