【Linux】之 UDP服务器与客户端

学习了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服务器有一些复杂的地方了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值