前段时间发了个TCP通信的例子,现在再来一个UDP通信的例子。这些可以作为样本程序,用到开发中。“裸写”socket老是记不住步骤,经常被鄙视……

下面的例子很简单,写一个UDP的server用于收包,写一个UDP的client用于发包并接收来自server的回复。其中UDP的client写了两个,一个是不需要connect的,另一个是带上connect的,两个client实现的功能是一样的。从效率上,带上connect的UDP肯定效率稍微高一些。不过UDP的connect和TCP里面非常不一样。在UDP里面connect的时候并没有三次握手的过程,但是它指定了与自己通信的对方的具体地址,内核中会将次地址记录下来,如果你的UDP就是在确定了两台机器之间传送信息,建议选取带有connect的套接字。connect之后与对方通信直接write或者read函数就可以,不用再指定对方ip和port,并且connect之后的套接字可以自动过滤掉不是来自指定通信方的信息。UDP可以调用多次connect函数,但是TCP套接字只能调用一次,再次调用会出现错误。

1. 首先是服务端的程序:

UDPserver.cpp

 #include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h> #define PORT 1234
#define MAXDATASIZE 100 int main(void)
{
int sockfd;
struct sockaddr_in server;
struct sockaddr_in client;
socklen_t len;
int num;
char buf[MAXDATASIZE];
if((sockfd = socket(AF_INET, SOCK_DGRAM, )) == -)
{
perror("Creating socket failed.\n");
exit();
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd, (struct sockaddr *)&server, sizeof(server)) == -)
{
perror("Bind() error.\n");
exit();
} len = sizeof(client);
while()
{
num = recvfrom(sockfd, buf, MAXDATASIZE, , (struct sockaddr *)&client, &len);
if(num < )
{
perror("recvfrom() error.\n");
exit();
}
buf[num] = '\0';
printf("You got a message <%s> from client. \nIt's ip is %s, port is %d. \n", buf, inet_ntoa(client.sin_addr),htons(client.sin_port));
sendto(sockfd, "Welcome\n", , , (struct sockaddr *)&client, len);
if ( !strcmp(buf, "bye") ){
break;
}
}
close(sockfd);
}

2. 然后,我们给出带有connect的客户端程序:

UDPclientWithConnect.cpp

 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h> #define PORT 1234 #define MAXDATASIZE 100 int main(int argc, char *argv[])
{
int sockfd, num;
char buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in server, peer;
if(argc != )
{
printf("Usage: %s <IP address> <message>\n", argv[]);
exit();
} if((sockfd=socket(AF_INET, SOCK_DGRAM, )) == -)
{
printf("socket() error\n");
exit();
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT); server.sin_addr.s_addr = inet_addr(argv[]);
//server.sin_addr.s_addr = inet_addr(argv[1]);
if(connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -)
{
printf("connect() error.\n");
exit();
} send(sockfd, argv[], strlen(argv[]), ); while()
{
if((num = recv(sockfd, buf, MAXDATASIZE, )) == -)
{
printf("recv() error.\n");
exit();
} buf[num] = '\0';
printf("Server Message: %s.\n", buf);
break;
}
close(sockfd);
}

3. 最后,再给一个不带connect的客户端程序。

UDPclientNoConnect.cpp

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> #define PORT 1234 #define MAXDATASIZE 100 int main(int argc, char *argv[])
{
int sockfd, num;
char buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in server, peer;
if(argc != )
{
printf("Usage: %s <IP address> <message>\n", argv[]);
exit();
}
if((he = gethostbyname(argv[]))==NULL)
{
printf("gethostbyname() error\n");
exit();
}
if((sockfd=socket(AF_INET, SOCK_DGRAM, )) == -)
{
printf("socket() error\n");
exit();
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr = *( (struct in_addr *)he->h_addr);
sendto(sockfd, argv[], strlen(argv[]), , (struct sockaddr *)&server, sizeof(server));
socklen_t len;
len = sizeof(server);
while()
{
if((num = recvfrom(sockfd, buf, MAXDATASIZE, , (struct sockaddr *)&peer, &len)) == -)
{
printf("recvfrom() error\n");
exit();
}
if(len != sizeof(server) || memcmp((const void *)&server, (const void *)&peer, len) != )
{
printf("Receive message from other server.\n");
continue;
}
buf[num] = '\0';
printf("Server Message: %s.\n", buf);
break;
}
close(sockfd);
}

执行一下命令进行编译:

$ g++ -o UDPserver UDPserver.cpp

$ g++ -o UDPclient2 UDPclientWithConnect.cpp

$ g++ -o UDPclient1 UDPclientNoConnect.cpp

完了以后就看到三个可执行文件了。

打开一个命令行,执行./UDPserver启动服务端程序,再打开另外一个命令行,执行./UDPclient1 127.0.0.1 "nihaonihao"或者./UDPclient2 127.0.0.1 "testtest"即可查看到以下效果:

[horstxu@vps ~/Cprog/udpCSmodel]$ ./UDPserver
You got a message <nihaonihao> from client.
It's ip is 127.0.0.1, port is 25595.
You got a message <testtest> from client.
It's ip is 127.0.0.1, port is 27396.
[horstxu@vps ~/Cprog/udpCSmodel]$ ./UDPclient1 127.0.0.1 "nihaonihao"
Server Message: Welcome
.
[horstxu@vps ~/Cprog/udpCSmodel]$ ./UDPclient2 127.0.0.1 "testtest"
Server Message: Welcome
.
[horstxu@vps ~/Cprog/udpCSmodel]$

最后再来解释一个带有connect的UDP的好处。由于UDP是不可靠传输,如果我发了数据出去,对方其实服务器是关闭的,这时会有什么结果呢?对于刚才的UDPclient1,也就是不带connect的,客户端程序会卡在recvfrom这里,因为对方是关闭的,它永远也收不到来自对方的回包。但是对于UDPclient2,也就是带有connect,我们其实可以收到一个错误,并设置errno(errno:111,connection refused)。这样看上去就比卡死在那里友好多了。对于这个问题的具体分析可以参考这篇文章:

UDP怎么会返回Connection refused错误

[horstxu@vps ~/Cprog/udpCSmodel]$ ./UDPclient2 127.0.0.1 "testtest"
recv() error.
[horstxu@vps ~/Cprog/udpCSmodel]$ ./UDPclient1 127.0.0.1 "testtest"
#注释:程序在这里卡死

[C语言]一个很实用的服务端和客户端进行UDP通信的实例的更多相关文章

  1. [C语言]一个很实用的服务端和客户端进行TCP通信的实例

    本文给出一个很实用的服务端和客户端进行TCP通信的小例子.具体实现上非常简单,只是平时编写类似程序,具体步骤经常忘记,还要总是查,暂且将其记下来,方便以后参考. (1)客户端程序,编写一个文件clie ...

  2. 编写一个简单的TCP服务端和客户端

    下面的实验环境是linux系统. 效果如下: 1.启动服务端程序,监听在6666端口上  2.启动客户端,与服务端建立TCP连接  3.建立完TCP连接,在客户端上向服务端发送消息 4.断开连接 实现 ...

  3. netcore 实现一个简单的Grpc 服务端和客户端

    参考资料,和详细背景不做赘述. 首先定义prop 文件 syntax ="proto3"; package RouteGrpc; service HelloWorld{ rpc S ...

  4. PHP socket服务端与客户端的简易通信

    今天学习socket通信的同时,顺便整理了下以前初识socket的知识. 现在关于php的socket通信,有些框架已经十分成熟了,比如  swoole 和 workerman,这两个大家可以学习学习 ...

  5. 关于一个WCF调用的服务端和客户端的配置信息集合

    客户端的配置我知道. 但是: httpTransport maxReceivedMessageSize="2147483647" <dataContractSerialize ...

  6. WCF开发的流程-服务端和客户端之间的通讯(内含demo讲解)

    讲解技术之前,恳请博友让我说几句废话.今天是我第一在博客园发布属于自己原创的博文(如有雷同,那是绝对不可能的事,嘿嘿).之前一直是拜读各位博友的大作,受益匪浅的我在这对博友们说声谢谢,谢谢你们的共享! ...

  7. 基于SignalR的服务端和客户端通讯处理

    SignalR是一个.NET Core/.NET Framework的实时通讯的框架,一般应用在ASP.NET上,当然也可以应用在Winform上实现服务端和客户端的消息通讯,本篇随笔主要基于Sign ...

  8. 常量,字段,构造方法 调试 ms 源代码 一个C#二维码图片识别的Demo 近期ASP.NET问题汇总及对应的解决办法 c# chart控件柱状图,改变柱子宽度 使用C#创建Windows服务 C#服务端判断客户端socket是否已断开的方法 线程 线程池 Task .NET 单元测试的利剑——模拟框架Moq

    常量,字段,构造方法   常量 1.什么是常量 ​ 常量是值从不变化的符号,在编译之前值就必须确定.编译后,常量值会保存到程序集元数据中.所以,常量必须是编译器识别的基元类型的常量,如:Boolean ...

  9. Node.js是一个事件驱动I/O服务端JavaScript环境

    Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎.目的是为了提供撰写可扩充网络程序,如Web服务.第一个版本由Ryan Dahl于2009年发布,后来,Jo ...

随机推荐

  1. mysql配置文件my.cnf详解

    原文地址:mysql配置文件my.cnf详解 作者:gron basedir = path 使用给定目录作为根目录(安装目录). character-sets-dir = path 给出存放着字符集的 ...

  2. day 2 系统分区 扩展.md

    1.分区类型 主分区: 最多只能有四个. 扩展分区: 最多只能有一个. 主分区加扩展分区最多有4个. 不能写入数据,只能包含逻辑分区. 逻辑分区 2.格式化 格式化(高级格式化)又称逻辑格式化,它是指 ...

  3. .Net程序员之Python基础教程学习----列表和元组 [First Day]

    一. 通用序列操作: 其实对于列表,元组 都属于序列化数据,可以通过下表来访问的.下面就来看看序列的基本操作吧. 1.1 索引: 序列中的所有元素的下标是从0开始递增的. 如果索引的长度的是N,那么所 ...

  4. 我的Github之旅(一)

    第一站:本地环境中的Github配置 1.参考链接 作为初学者,需要了解的有[本地环境中的github配置(基于mac)][1],以及git知识,这里推荐一个网站[猴子都能懂的Git入门][2],最后 ...

  5. Codeforces 549C. The Game Of Parity[博弈论]

    C. The Game Of Parity time limit per test 1 second memory limit per test 256 megabytes input standar ...

  6. [Flex] flex手机项目如何限制横竖屏?只允许横屏?

    flex手机项目如何限制横竖屏?只允许横屏?   有人知道吗?求教.. 工程中 xxx-app.xml 找到</aspectRatio> 去掉注释 修改为<aspectRatio&g ...

  7. BigDecimal.ROUND_HALF_XXX的各种用法

    在银行.帐户.计费等领域,BigDecimal提供了精确的数值计算.其中8种舍入方式值得掌握. 1.ROUND_UP 舍入远离零的舍入模式. 在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数 ...

  8. SVN的使用方法

    SVN的使用方法: 新建文件夹:文件夹1 在文件夹上点击右键--选择 SVN Checkout--弹出checkout窗口 下载文件的url获取:打开SVN--在要下载的文件上点击右键--点击Copy ...

  9. android整体架构概述--①

    android的logo 是由设计师去厕所时来的灵感. 其中android的命名都是以甜点的名字来定的. android的系统一共有四层. 1.Linux内核和驱动层 2.函数库  由C或C++编写 ...

  10. Eclipse如何解决启动慢

    一般在不对eclipse进行相关设置的时候,使用eclipse总是会觉得启动好慢,用起来好卡,其实只要对eclipse的相关参数进行一些配置,就会有很大的改善. 加快启动速度  1.在eclipse启 ...