網絡編程16——UDP實現的併發服務器和客戶端

TCP、UDP通信的各自優缺點

TCP: 面向連接的, 可靠,數據報傳輸。對於不穩定的網絡層,採取完全彌補的通信方式,即丟包重傳
優點:穩定,數據流量穩定、速度穩定、順序穩定
缺點:傳輸速度慢,速率低,開銷大
使用場景:數據的完整性要求較高,不追求效率,如大數據傳輸,文件傳輸

UDP:無連接的,不可靠的,數據報傳輸,對於不穩定的網絡層,採取完全不彌補的通信方式。默認還原網絡狀況
優點:傳輸速度快,速率高,開銷小
缺點:不穩定,數據流量、速度、順序都不穩定
使用場景:對時效性要求較高,對穩定性其次,如遊戲、視頻會議、視頻電話

UDP實現的CS模型:

recv()/send()函數只能用於TCP通信,替代read、write
server:不用三次握手去建立連接了,connect()被捨棄,accept()被捨棄
1,lfd = socket(AF_INET, SOCK_DGRAM,0); //第二個參數SOCK_DGRAM表示報式協議,若STREAM則表示流式協議
2,bind();綁定地址結構
3,listen();同時設置三次握手的數量,所以listen可有可無,用不上
4,while(1){
read(cfd, buf,) //read函數被替換,用recvfrom()
recvfrom();
toupper();
write()也被替換,用sendto()
}
5,close();

client:
1,connfd = socket(AF_INET, SOCK_DGRAM,0); //報式協議
2,sendto(connfd,“服務器的地址結構”, 地址結構長度); //代替conncet();
3,recvfrom();//想成原來的read()就行
4,寫到屏幕
5,close();

recvfrom()函數和sendto()函數

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
看參數,所以recvfrom函數不單替代了read()函數的功能呢,還涵蓋了accept()返回地址結構的作用
sockfd:lfd
buf:緩衝區地址
len:緩衝區大小
flags:默認0
src_addr:(struct sockaddr*)&addr傳出參數,傳出對端地址結構
addrlen:傳入傳出參數
**返回值:**成功接收數據字節數;失敗-1 errno;對端關閉0

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:套接字
buf:存儲數據的緩衝區地址
len:緩衝區大小
flags:默認0
src_addr:傳入參數,目標地址結構(要將數據發送給哪個服務器
addrlen:地址結構長度
**返回值:**成功寫出數據字節數;失敗-1 errno;對端關閉0

UDP實現的併發服務器和客戶端

server端👇:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<ctype.h>

#define SERV_PORT 8000

int main(void)
{
	int sockfd;
	struct sockaddr_in serv_addr, clie_addr;
	socklen_t clie_addr_len;
	int i, n;
	char buf[BUFSIZ];
	char str[INET_ADDRSTRLEN];//在網路地址轉換時用inet_ntop()
//①
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//②
	bzero(&serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(SERV_PORT);
	bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
	printf("Accepting connection...\n");
//捨去listen函數,不用accept,直接就recvfrom和sendto
	while(1)
	{
		clie_addr_len = sizeof(clie_addr);
		n = recvfrom(sockfd, buf,BUFSIZ,0,(struct sockaddr*)&clie_addr, &clie_addr_len);//從客戶端中去讀取
		if(n == -1)
			perror("recvfrom error");
		printf("received from %s at PORT %d \n", inet_ntop(AF_INET, &clie_addr.sin_addr, sizeof(str)), ntohs(clie_addr.sin_port));
//讀
		for(i = 0; i<n; i++)
			buf[i] = toupper(buf[i]);
//操作
		n = sendto(sockfd, buf, n, 0, (struct sockaddr*)&clie_addr, sizeof(clie_addr));
		if(n == -1)
			perror("sendto error");	
	}
	close(sockfd);
	return 0;
}

client端👇:

#include<stdio.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>

#define SERV_PORT 8000

int main(int argc, char *argv[])
{
	int sockfd, n;
	struct sockaddr_in servaddr;//只要去連接的服務器的地址就行了
	char buf[BUFSIZ];
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	bzero(&servaddr, sizeof(servaddr));
	servadd.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1",&serv_addr.sin_addr);	//服務器的ip地址?傳網路地址
	servaddr.sin_port = htons(SERV_PORT);
	bind(sockfd,(struct sockaddr*)&servaddr, sizeof(servaddr));
	//客戶端就先寫sendto,再讀回來
	while(fgets(buf,BUFSIZ,stdin) != NULL)
	{
		n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&servaddr, sizeof(servaddr));
		if(n == -1)
			perror("sendto error");
		n = recvfrom(sockfd, buf, BUFSIZ,0,NULL,0);//NULL表示不關心對端信息
		if(n ==-1)
			perror("recvfrom error");
		write(STDOUT_FILENO, buf, n);	
	}
	close(sockfd);
	return 0;
}

在這裏插入圖片描述
在這裏插入圖片描述
bind()上兩個函數幾乎是一樣的,就是對serv_addr初始化的時候對服務器的ip地址轉換不同,服務器就是htonl(INADDR_ANY); 對客戶端需要去將已知的ip地址比如"127.0.0.1"轉換成網絡傳輸的用到inet_pton()函數

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