网络编程复习(UDP)----和服务器说 hello!

网络编程复习(UDP)----和服务器说 hello!

说回TCP

今天再说UDP之前 , 咱们先说回TCP , TCP在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立通过三次握手,释放则需要四次握手,所以说每个连接的建立都是需要资源消耗和时间消耗的。

1. TCP短连接
模拟一种TCP短连接的情况:
	client 向 server 发起连接请求
	server 接到请求,双方建立连接
	client 向 server 发送消息
	server 回应 client
	一次读写完成,此时双方任何一个都可以发起 close 操作	
在步骤5中,一般都是 client 先发起 close 操作。当然也不排除有特殊的情况。
从上面的描述看,短连接一般只会在 client/server 间传递一次读写操作!
2. TCP长连接
再模拟一种长连接的情况:
	client 向 server 发起连接
	server 接到请求,双方建立连接
	client 向 server 发送消息
	server 回应 client
	一次读写完成,连接不关闭
	后续读写操作...
	长时间操作之后client发起关闭请求
3. TCP长/短连接操作过程
3.1 短连接的操作步骤是:
	建立连接——数据传输——关闭连接...建立连接——数据传输——关闭连接

3.2 长连接的操作步骤是:
	建立连接——数据传输...(保持连接)...数据传输——关闭连接
4. TCP长/短连接的优点和缺点
长连接可以省去较多的TCP建立和关闭的操作,减少浪费,节约时间。
对于频繁请求资源的客户来说,较适用长连接。
client与server之间的连接如果一直不关闭的话,会存在一个问题,
随着客户端连接越来越多,server早晚有扛不住的时候,这时候server端需要采取一些策略,
如关闭一些长时间没有读写事件发生的连接,这样可以避免一些恶意连接导致server端服务受损;
如果客户请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽。
5. TCP长/短连接的应用场景
长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。
每个TCP连接都需要三次握手,这需要时间,如果每个操作都是先连接,
再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,
再次处理时直接发送数据包就OK了,不用建立TCP连接。
例如:数据库的连接用长连接,如果用短连接频繁的通信会造成socket错误,
而且频繁的socket 创建也是对资源的浪费。
而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,
而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,
如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,
那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。

说回UDP

Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。

UDP作为一个面向无连接的传输协议 , 比较简单 , 下面来看看他的实现过程:

UDP服务器
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

//UDP server
#define IP  "127.0.0.1"
#define SPORT 60000
#define SIZE  100

int socketInit(const char * ip, unsigned short port);

int main()
{
   int socketID = 0;
   ssize_t ret = 0;
   char buffer[SIZE] = {0};
   int addrLength = 0;
   struct sockaddr_in addr;
   
   socketID = socketInit("127.0.0.1", 60000);
   //等待数据(接收消息)
   addrLength = sizeof(addr);
   ret = recvfrom(socketID, buffer, SIZE - 1, 0, (struct sockaddr *)&addr, &addrLength);
   if ( 0 > ret )
   {
   	perror("recvfrom error");
   }
   else 
   {
   	//处理消息
   	printf("server get data:%s, from port:%d\r\n", buffer, ntohs(addr.sin_port));
   }
   
   //作出回应
   fgets(buffer, SIZE - 1, stdin);	
   ret = sendto(socketID, buffer, strlen(buffer), 0, (struct sockaddr *)&addr, addrLength);
   if ( 0 > ret )
   {
   	perror("server send message error");
   }
   else 
   {
   	printf("服务器回复客户端:%s\r\n", buffer);
   }
   //关闭socket
   close(socketID);
   return 0;
}

int socketInit(const char * ip, unsigned short port)
{
   int socketID = 0;
   int on = 1;
   
   //创建socket 
   socketID = socket( PF_INET, SOCK_DGRAM, 0 );
   if ( 0 > socketID )
   {
   	perror("socket error");
   	exit(0);
   }
   printf("socket ok\r\n");
   
   if (0 > setsockopt(socketID, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
   {
   	perror("set sock opt reUseAddr error");
   }
   
   //绑定自己的ip+port
   struct sockaddr_in addr = {
   	.sin_family = PF_INET,
   	.sin_port   = htons(port),
   	.sin_addr   = {
   		.s_addr = INADDR_ANY,
   	},
   };
   if ( 0 > bind(socketID, (struct sockaddr *)&addr, sizeof(addr)))
   {
   	perror("bind error");
   	exit(0);
   }
   printf("bind ok\r\n");
   return socketID;
}
UDP客户端
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

//UDP client
#define IP  "127.0.0.1"
#define SPORT 60000
#define SIZE  100

int main()
{
	int socketID = 0;
	char buf[SIZE] = {0};
	int addrLength = 0;
	struct sockaddr_in addr;
	ssize_t ret = 0;
	
	//创建socket 
	socketID = socket( PF_INET, SOCK_DGRAM, 0 );
	if ( 0 > socketID )
	{
		perror("socket error");
		return -1;
	}
	printf("client socket ok\r\n");
	
	//准备要发送的消息
	fgets(buf, SIZE - 1, stdin);
	//设置对方的地址
	addrLength = sizeof(addr);
	memset(&addr, 0, addrLength);
	addr.sin_family = PF_INET;
	addr.sin_port = htons(SPORT);
	addr.sin_addr.s_addr = inet_addr(IP);
	//发送消息
	ret = sendto( socketID, buf, strlen(buf), 0, (struct sockaddr *)&addr, addrLength);
	if ( 0 > ret )
	{
		perror("send to error");
	}
	memset(buf, SIZE, 0);
	//等待对方的回应
	ret = recvfrom(socketID, buf, SIZE - 1, 0, (struct sockaddr *)&addr, &addrLength);
	if ( 0 > ret )
	{
		perror("recvfrom error");
	}
	else 
	{
		//处理消息
		printf("client get data:%s, from port:%d\r\n", buf, ntohs(addr.sin_port));
	}
	
	//关闭socket
	close(socketID);
	return 0;
}
补充
1. 阻塞
	accept/read/recv/recvfrom/send/sendto/write
	在默认情况下打开一个设备文件时,都是阻塞打开。
	缓冲区为空,读不能立即返回,就是读阻塞。
	缓冲区为满,写,不能立即返回,就是写阻塞。
	特点:最常用、最简单、效率最低。
2. 非阻塞
	缓冲区为空,读能立即返回,读不阻塞。
	缓冲区为满,写能立即返回,写不阻塞。
	多用轮询的办法 
	特点:采用轮询的办法,比较耗CPU
3. 信号驱动
	特点:一种异步通信模型。需要底层驱动的配合。
4. 多路复用
	允许同时对多个I/O进行控制
5. 阻塞和非阻塞之间的切换

	int fcntl(int fd, int cmd, long arg);
    int flag;
	//获取当前打开的文件描述符的状态标志位,第三个参数可以忽略
    flag = fcntl(sockfd, F_GETFL, 0);
    flag |= O_NONBLOCK;
	//设置当前打开的文件描述符的状态标志位。第三个参数就是该文件的新的描述符状态标志
    fcntl(sockfd, F_SETFL, flag);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值