插座第三個參數傳0,表示默認協議,流式協議的典型代表就是TCP協議,報式協議的典型代表就是UTP協議。
名詞解釋:
BSD:(Berkeley Software Distribution 伯克利發佈的軟件套件),主要是網絡部分。
本地套接字:
本地套接字是的的的的Unix中的中進程間通訊方式,支持進程間的通信,但不能使用在跨計算機的通信中,服務端與客戶端的通信流程基本與網絡套接字一致,但在形式上有兩點不同:
- 創建插座時,傳入的域參數不同。域參數應該傳入本地套接字AF_UNIX,AF_LOCAL或AP_UNIX,PF_LOCAL。
- AF:地址族,一般傳入地址時使用。
- PF:協議族,一般傳入協議時使用。
- 但是宏定義的值相同,一般可以混用。
- 綁定的地址需要使用sockaddr_un結構體,struct sockaddr_un結構有兩個參數:sun_family,sun_path.sun_family只能是AF_LOCAL或AF_UNIX,而sun_path是本地文件的路徑。通常將文件放在/ tmp目錄下。
- addr.sun_family = AF_UNIX;
- 的的的strcpy(addr.sun_path中中中,“server.sock”);
- ret = bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));
- 所以還需要對文件進行驗證,如果文件存在綁定就會報錯。
addr.sun_family = AF_UNIX;
的strcpy(addr.sun_path中,“server.sock”);
ret = bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));
參考:https://www.cnblogs.com/hnrainll/archive/2011/04/24/2026433.html
Linux下的UDP套接字編程
UDP協議一般用於即時通訊的應用開發,它們對於數據的及時性要求較高,TCP的連接,驗證等機制都會降低數據的傳送效率。但考慮到數據的完整性問題,一些大型網絡公司就會使用到udp + tcp的形式,在傳輸層使用UDP,在應用層使用自定義的協議,添加類似於TCP的校驗方式,彌補UDP的丟包現象。
彌補UDP丟包的應用層方法,可以改變數據接收端緩衝區的大小,儘量減少緩衝區已滿造成數據丟失的現象。
int n = 220 * 1024;//這是一個在應用中得出的最優值。
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
兩個函數:服務端參數:
recvfrom(connfd,buf,strlen(buf),0,(struct sockaddr *)&addr,&len); //此處的地址與LEN都是傳入傳出參數,獲取的客戶端的信息,用作之後的SENDTO函數參數。
sendto(sockfd,buf,n,0,(struct sockaddr *)&addr,len); //按照指定的地址將數據發送給客戶端,所以此處的LEN參數不需要取地址。若客戶端不先行發送數據,服務端就無法得知其地址,無法主動發送數據。
UDP實現廣播
TCP / IP協議棧中,只有UDP可以廣播。
網管地址(網管號):主機號爲全0的IP地址所有訪問外網的請求都由這個網管轉發,當然也可以在此網關處設置限制。
廣播地址:主機號爲全1的IP地址爲廣播地址當發出一個目的地址爲廣播地址的分組(封包)時,它將被分發給該網段上的所有計算機。
IP:192.168.42.255(廣播)
IP:192.168.42.1(網管)
的的sockfd默認不支持廣播,需要通過調用setsockopt的的函數進行修改,開放廣播權限。
flag = 1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));
需要注意:
- 127.0.0.0是本地會環地址,從網卡的發送端出去,在本地接收端返回,可以用於測試本地的C / S程序。
- 在服務器中,0.0.0.0指的是本機上的所有(任意)IPV4地址,如果一個主機有兩個IP地址,192.168.1.1和10.1.2.1,並且該主機上的一個服務監聽的地址是0.0 .0.0,那麼通過兩個IP地址都能夠訪問該服務。當然在服務器端也可以使用,還可以用INADDR_ANY(宏定義值爲0)來代替。
- 然而對於Linux的發行版本的實際實現中,需要使用客戶端需要使用任意地址,不然使用本地某個具體地址就會接收不到廣播。
- 在同一局域網內的不同主機可以擁有兩個相同的IP,即IP衝突。
- 在同一臺計算機中的IP地址與端口號不能在程序運行時重用,在不同計算機中可以使用相同的端口號實現廣播,發送方向也可以指向同一端口號。
注意:客戶端地址需要使用任意,否則接收不到廣播信息。這是必須的。
貼出以下實現代碼:
server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define SIZE 128
#define BROADCAST_IP "192.168.16.255" //C類IP地址
#define CLIENT_PORT 9000
#define SERVER_PORT 8000
int main(void)
{
int ret = -1;
int sockfd = -1;
int flag = -1;
char buf[SIZE];
struct sockaddr_in addr;
struct sockaddr_in from;
socklen_t len = sizeof(from);
//1.創建套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd) {
perror("socket");
return 1;
}
printf("sockfd :%d\n", sockfd);
//2.綁定
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(SERVER_PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
if (-1 == ret) {
perror("bind");
return 1;
}
flag = 1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));
/* 構造client地址 IP + 端口,因爲是服務器主動發送數據,
* 所以需要知道客戶端的端口號,與之前基於TCP的C/S模型相比,
* 之前的一般由客戶端主動訪問服務端,而客戶端的端口號是隨機獲取的。
* 而對於廣播,是一種不同的業務需求,
* 需要服務端向範圍IP端口發送數據,這時候就很有必要知道客戶端程序的端口號。
* 所以此時服務端的端口號反倒不重要了,可以隨機獲取。*/
memset(&from, 0, sizeof(from));
from.sin_family = AF_INET;
from.sin_port = htons(9000);
inet_pton(AF_INET, BROADCAST_IP, &from.sin_addr.s_addr);
//3.循環接收客戶端數據
int i = 0;
// memset(buf, 0, SIZE);
while (1) {
sprintf(buf, "Drink %d glasses of water", i++);//有一端添加換行符即可。
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&from, sizeof(from));
printf("--->發出數據\n");
usleep(100000);
}
//4.關閉文件描述符
close(sockfd);
return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define SIZE 128
#define CLIENT_PORT 9000
int main(void)
{
int ret = -1;
int sockfd = -1;
char buf[SIZE];
struct sockaddr_in addr;
int len = -1;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd) {
perror("socket");
return 1;
}
//初始化本地地址
//相當於INADDR_ANY
//區分127.0.0.1
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(CLIENT_PORT);
ret = inet_pton(AF_INET, "0.0.0.0", &addr.sin_addr.s_addr);
if (-1 == ret) {
printf("inet_pton");
return 1;
}
ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
if (0 == ret)
printf("...bind ok...\n");
memset(buf, 0, SIZE);
/* 從網絡中讀取的數據,傳輸過來的字符串結束標記的'\0'是會被忽略的。
* 所以一定要注意將接受端的容器清空,這樣才能夠保證不讀取客戶端未
* 清空的buf後的錯誤數據。所以,最重要的問題就是:
* 1.網絡傳輸中尾0被忽略。
* 2.客戶端接收時沒有將容器即使清零。*/
while (1) {
len = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, 0);
printf("ret = %d\n", len);
printf("--->%s\n", buf);
}
close(sockfd);
return 0;
}
TCP UDP與
TCP:面向連接的可靠數據包傳遞------對當前網絡環境做完全彌補
優點:穩定
- 數據穩定:------丟包回傳的回執機制。
- 速率穩定:------使用TCP方式傳遞的數據包在網絡穩定的情況下經過的路由是固定的。
- 流量穩定:------滑動窗口,對每次發送的數據量進行限制。
缺點:
- 效率低:回執
- 傳輸速度慢:滑動窗口
使用場景:
- 大文件傳輸
- 重要文件傳輸
UDP:無連接的不可靠報文傳遞------對當前網絡環境做完全不彌補,還原當前的網絡狀態。
優點:效率高,速度快
缺點:不穩定:數據,速率,流量。
使用場景:對實時性要求較高。