學習了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服務器有一些複雜的地方了。