UDP通信流程步骤:

服务端: 等待(被动)接收发送

1: 创建 socket:  socket()

2: 绑定端口:      bind()

3: 读取消息:      read()

4: 发送消息:      write()

5: 关闭套接字:  close()

客户端:主动发送接收

1: 创建 socket:   socket()

2: 发送数据:        write()

3: 接受结果:         read()

4: 关闭套接字:     close()

UDP通信流程图:

UDP通信

1.没有固定连接

2.客户端发完包,就不管了,也不知道服务端是不是收到了

UDP创建套接字、绑定套接字的方式与TCP一样,可参考TCP。

发送消息

sendto(int sockfd, void* buf,  size_t len,  int flags,

struct sockaddr *to,  socklen_t tolen);

sockaddr由sockaddr_in转换。

UDP 没有accept创建新的通信fd,需要指定目标地址

函数可以用于TCP通信,后面两个参数会忽略

接收消息

recvfrom(int sockfd, void *buf,  size_t len ,  int flags,

sturct sockaddr *from,  socklen_t *fromlen)

UDP 没有 accept 函数来获取对端地址,这里增加了2个参数

函数可以用于TCP通信

例子:

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<arpa/inet.h>

#include<unistd.h>

#define SRV_PORT  8888

#define CLT_PORT  6666

void Udp_server()

{

int fd;

int iRet;

struct sockaddr_in addr;

socklen_t addrlen = sizeof(addr);

//创建套接字

fd = socket(PF_INET, SOCK_DGRAM, 0);

if (fd < 0)

{

perror("Fail to socket!");

return;

}

addr.sin_family = AF_INET;

addr.sin_port = htons(SRV_PORT);

addr.sin_addr.s_addr = htonl(INADDR_ANY);

//绑定,可以让客户端知道通过什么IP地址和端口号来连接

iRet = bind(fd, (struct sockaddr*)&addr, addrlen);

if (iRet)

{

perror("Fail to bind!");

close(fd);

return;

}

struct sockaddr_in srcaddr;

char szBuf[1000];

char szMsg[] = "[UDP]I Received!";

while(1)

{

//接收,并获取客户端的IP和端口号(struct sockaddr*)&srcaddr

memset(szBuf, 0, 1000);

iRet = recvfrom(fd, szBuf, 1000, 0, (struct sockaddr*)&srcaddr, &addrlen);

if (iRet < 0)

{

perror("Fail to recvfrom!");

break;

}

printf("Recv:%s\n", szBuf);

//发送

fprintf(stderr,"Echo:");

scanf("%s",szMsg);

sendto(fd, szMsg, strlen(szMsg), 0, (struct sockaddr*)&srcaddr, addrlen);

}

close(fd);

return;

}

void Udp_client()

{

int fd;

int iRet;

struct sockaddr_in addr;

socklen_t  addrlen = sizeof(addr);

fd = socket(PF_INET, SOCK_DGRAM, 0);

if (fd < 0)

{

perror("Fail to socket!");

return;

}

/*

addr.sin_family = AF_INET;

addr.sin_port = htons(CLT_PORT);

addr.sin_addr.s_addr = htonl(INADDR_ANY);

iRet = bind(fd, (struct sockaddr*)&addr, addrlen);

if (iRet)

{

perror("Fail to bind!");

close(fd);

return;

}

*/

struct sockaddr_in srvaddr;

char szIp[16] = ;

int  port;

fprintf(stderr, "Input server IP and port:");

scanf("%s%d", szIp, &port);

srvaddr.sin_family = AF_INET;

srvaddr.sin_port = htons((short)port);

srvaddr.sin_addr.s_addr = inet_addr(szIp);

char szBuf[100];

char szRcv[1000];

while(1)

{

memset(szBuf, 0, 100);

fprintf(stderr, "->");

read(STDIN_FILENO, szBuf, 100);

sendto(fd, szBuf, strlen(szBuf), 0, (struct sockaddr*)&srvaddr, addrlen);

memset(szRcv, 0, 1000);

iRet = recvfrom(fd, szRcv, 1000, 0, (struct sockaddr*)&srvaddr, &addrlen);

if (iRet < 0)

{

perror("Fail to recvfrom!");

break;

}

printf("Recv:%s\n", szRcv);

}

close(fd);

return;

}

int main(int argc, char** argv)

{

if (argc!=2

|| (strcmp(argv[1], "s") && strcmp(argv[1], "c"))

)

{

printf("Usage: %s [ c | s ]\n", argv[0]);

printf("\t s: For start udp server\n");

printf("\t c: For start udp client\n");

return 0;

}

if (argv[1][0] == 's')

{

Udp_server();

}

else if (argv[1][0] == 'c')

{

Udp_client();

}

return 0;

}

UDP打洞:

打洞就是让对方不需要通过服务器的转换,直接使用公网IP地址进行通信。

解析:

一般的主机都是使用私有IP地址(在不同的局域网中私有IP地址可以重复的),对方主机通过路由路转换成公有IP地址,再进行通信。

打洞,当主机A向某一服务器发送数据时,此时在服务器上,显示的是主机A的公网IP地址和端口号,要做的就是将该IP地址和端口号再一次返回给主机A。那么主机A就知道了自己的公网IP和端口号。此时主机B就可以直接对主机A发送数据,进而可以相互通信。

28UDP的更多相关文章

随机推荐

  1. 漫游Kafka设计篇之性能优化(7)

    Kafka在提高效率方面做了很大努力.Kafka的一个主要使用场景是处理网站活动日志,吞吐量是非常大的,每个页面都会产生好多次写操作.读方面,假设每个消息只被消费一次,读的量的也是很大的,Kafka也 ...

  2. $_SERVER,IP,域名常用方法

    PHP $_SERVER详解   $_SERVER['HTTP_ACCEPT_LANGUAGE']//浏览器语言 $_SERVER['REMOTE_ADDR'] //当前用户 IP . $_SERVE ...

  3. Hibernate_day02--Hibernate的一级缓存

    Hibernate的一级缓存 什么是缓存 1 数据存到数据库里面,数据库本身是文件系统,使用流方式操作文件效率不是很高. (1)把数据存到内存里面,不需要使用流方式,可以直接读取内存中数据 (2)把数 ...

  4. UE4关于编译配置的参考(Debug,DebugGame,Development,Shipping,Test等)

    https://docs.unrealengine.com/latest/CHN/Programming/Development/BuildConfigurations/index.html 编译配置 ...

  5. SQL Server计算列

    计算列由可以使用同一表中的其他列的表达式计算得来.表达式可以是非计算列的列名.常量.函数,也可以是用一个或多个运算符连接的上述元素的任意组合.表达式不能为子查询. 例如,在 AdventureWork ...

  6. TFS 创建团队成员及管理

    用TFS服务器管理代码 1:创建TFS服务器 2:创建TFS项目 3:创建TFS团队成员 4:上传代码,共享链接,对代码进行管理 这里主要介绍第三步:创建TFS团队成员 第一种:通过连接进入TFS可视 ...

  7. 【BZOJ2827】千山鸟飞绝 hash+堆+SBT

    [BZOJ2827]千山鸟飞绝 Description 话说有一天doyouloveme和vfleaking到山里玩.谁知doyouloveme刚刚进山,所有的鸟儿竟被他的神犇气场给惊得全部飞走了.v ...

  8. No such file or directory

    项目-targets-build phases, 然后从copy bundle resources里面删掉红色的错误资源文件

  9. Android 关于异步Http请求,以及编码问题

    大家都知道可以使用一个继承了AsyncTask的类去实现异步操作,再有个Http请求的类就可以解决了,现在我说下里面的细节问题,比如长时间无反应,编码问题,以及一些HTML相关的处理. 首先说下长时间 ...

  10. PHP 支持8种基本的数据类型。

    四种标量类型:boolean (布尔型):这是最简单的类型,只有两种取值,可以为 TRUE/true 或 FALSE/false ,不区分大小写.详细请查看:PHP布尔类型(boolean)integ ...