Linux环境编程 基于UDP通信协议的回声服务器

套接字(Socket)

socket是一种可以进行网络通信的内核对象,它有一个唯一的标识符,一般称它为socket描述符——sockfd,可类比于文件描述符fd,基于Linux下一切皆文件的概念,所以sockfd也可以用read/write/close操作。


socket函数:创建socket对象

int socket(int domain, int type, int protocol);

domain:通信地址类型
  AF_UNIX/AF_LOCAL:本地进程间通信
  AF_INET:使用ipv4地址通信
  AF_INET6:使用ipv6地址通信

type:socket对象类型
SOCK_STREAM:数据流协议,TCP(面向连接的通信协议)。特点是安全可靠,数据不会丢失,但速度慢。常用于安全性较高的场景;
SOCK_DGRAM:数据报协议,UDP(面向无连接的通信协议)。特点是速度快,数据可能丢失,安全性和可靠性与tcp相比不同。一般用于安全性要求不高但是对速度有要求的场景。

protocol:特殊协议
  较少使用,一般直接写0

返回值:成功返回非负描述符,失败返回-1


网络通信地址

struct sockaddr_in
  {
   // 通信地址类型
   short int sin_family;
   // 端口号
   in_port_t sin_port;
   // ip地址
   struct in_addr sin_addr;
  }

bind函数:把socket对象与通信地址建立联系

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

sockfd:socket对象的描述符,即socket函数的返回值
struct sockaddr* addr指定了想要绑定的ip和端口号,均用网络字节序-即大端模式;
addrlen是前面struct sockaddr(与sockaddr_in等价)的长度
返回值: 成功返回0,失败返回-1


connect函数:连接通信目标

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

个人计算机系统数据的存储方式可能是大端,也可能是小端,网络通信时需要的是大端数据,必须把数据转换成大端。

uint32_t htonl(uint32_t hostlong);//功能:把32位的主机字节序转换成32位网络字节序

uint16_t htons(uint16_t hostshort);//功能:把16位的主机字节序转换成16位网络字节序

uint32_t ntohl(uint32_t netlong);//功能:把32位网络字节序转换成32位的主机字节序

uint16_t ntohs(uint16_t netshort);//功能:把16位网络字节序转换成16位的主机字节序

生成端口号:
端口号就是一个16位的无符整数(065536),一般设置为大于1024的值,11023为保留端口号。
通常使用htons()函数来获取端口号。


生成ip地址:
功能:把点分十进制的字符串ip地址转换成32位的无符号整数

in_addr_t inet_addr(const char *cp);

功能:把32的的网络字节序的ip地址转换成点分十进制的字符串ip地址。

char *inet_ntoa(struct in_addr in);

recvfrom函数:接收数据并获取发送端的地址

 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

buf:数据缓冲区
len:缓冲区的大小
flag:通常为0
src_addr:数据来源端的地址
*addrlen:src_addr的长度


sendto函数:发送数据到指定的目标

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

dest_addr:数据要发送的目标地址


UDP编程模型:
进程A:创建socket对象->准备地址->绑定->接收数据和来源的地址->原路返回数据->关闭socket。
进程B:创建socket对象->准备地址->向目标发送数据->接收数据->关闭socket。

使用UDP协议实现双向传输数据(通过ip地址和端口,既可以与自己也可以与别人通信)。

服务端

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

typedef struct sockaddr* SP;

int main(int argc,const char* argb[])
{
	// 创建socket
	int svr_sock = socket(AF_INET,SOCK_DGRAM,0);
	if(0 > svr_sock)
	{
		perror("socket");
		return -1;
	}

	// 准备通信地址(自己的)
	struct sockaddr_in svr_addr = {},cli_addr = {};
	svr_addr.sin_family = AF_INET;
	svr_addr.sin_port = htons(7788);
	svr_addr.sin_addr.s_addr = inet_addr("172.20.10.2");
	socklen_t addrlen = sizeof(svr_addr);

	// 绑定地址与socket对象
	if(bind(svr_sock,(SP)&svr_addr,addrlen))
	{
		perror("bind");
		return -1;
	}

	char buf[4096]={};
	char buf2[4096]={};
	size_t buf_size = sizeof(buf);
	for(;;)
	{
		// 接收数据和来时的地址
		size_t ret_size = recvfrom(svr_sock,buf,buf_size,0,(SP)&cli_addr,&addrlen);
		if(ret_size <= 0)
		{
			perror("recvform");
			close(svr_sock);
			return -1;
		}
		printf("from:%s  recv:%s  bits:%d\n",inet_ntoa(cli_addr.sin_addr),buf,ret_size);
		sprintf(buf2,",return:%s",inet_ntoa(svr_addr.sin_addr));
		strcat(buf,buf2);
		// 返回数据
		sendto(svr_sock,buf,strlen(buf)+1,0,(SP)&cli_addr,addrlen);
	}
}

客户端

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

typedef struct sockaddr* SP;

int main(int argc,const char* argb[])
{
	// 创建socket对象
	char is_broadcast = 1;
	int cli_sock = socket(AF_INET,SOCK_DGRAM,0);
	if(0 > cli_sock)
	{
		perror("socket");
		return -1;
	}
	setsockopt(cli_sock,SOL_SOCKET,SO_BROADCAST,&is_broadcast,1);
	
	// 准备通信地址(服务端)
	struct sockaddr_in svr_addr;
	svr_addr.sin_family = AF_INET;
	svr_addr.sin_port = htons(7788);
	svr_addr.sin_addr.s_addr = inet_addr("172.20.10.2");
	socklen_t addrlen = sizeof(svr_addr);

	char buf[4096] = {};
	size_t buf_size = sizeof(buf);
	
	for(;;)
	{
		printf(">>>");
		gets(buf);
		if(0 == strcmp("quit",buf))
		{
			printf("已退出通信.");
			close(cli_sock);
			return EXIT_SUCCESS;
		}
		// 向服务器发送数据
		sendto(cli_sock,buf,strlen(buf)+1,0,(SP)&svr_addr,addrlen);
		// 从服务器接收数据
		size_t ret_size = recvfrom(cli_sock,buf,buf_size,0,(SP)&svr_addr,&addrlen);
		if(0 >= ret_size)
		{
			perror("recvfrom");
			close(cli_sock);
			return EXIT_FAILURE;
		}
		printf("from:%s recv:%s bits:%d\n",inet_ntoa(svr_addr.sin_addr),buf,ret_size);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值