udp通信 c 语言,Windows程序设计 | 基于UDP的C/S通讯实现

2023-05-16

作者小序--

今天翘课一天,本打算一个早上写完网络编程实验报告就出去溜达,结果从早上九点到这会没出过宿舍门,宅到不能再宅了,哎~,一首凉凉送给自己。。。。

分享一下干货吧~,windows下的网络编程和linux下的网络编程基本差不多,因为协议的实现机制完全相同,只是一些小细节上的区别,今天主要就说一下UDP数据报协议的实现原理:

接收端:1、创建数据报套接字;2、绑定本机地址和端口;3、等候接收数据;4、使用完成后关闭套接字。

发送端:1、创建数据报套接字;2、向指定地址和端口发送数据;3、使用完成后关闭套接字。

就是这么简单,不需要像传输控制协议那样,通过connect函数进行创建链接并进行三次握手四次挥手,只需要创建自己的套接字就可以进行通讯,但上帝是公平的,UDP不需要进行连接,也导致他的可靠性降低,很简单一个例子,如果客户需要给服务器发送消息,结果服务器这时候没有打开,而客户会以为消息发送成功,就会导致数据丢失。所以说没有什么模型是完美的,只能说取其精华,弃其糟粕罢了!

1、认真理解数据报套接字编程模型,在仔细阅读并调试运行UDPserve.cpp程序和UTPClient.cpp程序源代码,分析在服务端和客户端分别使用了如下的Winsock

API函数;

(1)socket函数:为了执行网络输入输出,一个进程必须做的第一件事就是调用socket函数获得一个文件描述符。

-----------------------------------------------------------------#include

int

socket(int family,int type,int protocol);返回:非负描述字---成功-1---失败-----------------------------------------------------------------

第一个参数指明了协议簇,目前支持5种协议簇,最常用的有AF_INET(IPv4协议)和AF_INET6(IPv6协议);第二个参数指明套接口类型,有三种类型可选:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和SOCK_RAW(原始套接口);如果套接口类型不是原始套接口,那么第三个参数就为0。

(2)bind函数:为套接口分配一个本地IP和协议端口,对于网际协议,协议地址是32位IPv4地址或128位IPv6地址与16位的TCP或UDP端口号的组合;如指定端口为0,调用bind时内核将选择一个临时端口,如果指定一个通配IP地址,则要等到建立连接后内核才选择一个本地IP地址。

-------------------------------------------------------------------#includeint

bind(int sockfd, const struct sockaddr * server, socklen_t

addrlen);返回:0---成功-1---失败-------------------------------------------------------------------

第一个参数是socket函数返回的套接口描述字;第二和第第三个参数分别是一个指向特定于协议的地址结构的指针和该地址结构的长度。

(3)recvfrom函数:UDP使用recvfrom()函数接收数据,他类似于标准的read(),但是在recvfrom()函数中要指明目的地址。

-------------------------------------------------------------------#include#includessize_t

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

sockaddr * from, size_t *addrlen);返回接收到数据的长度---成功-1---失败-------------------------------------------------------------------

前三个参数等同于函数read()的前三个参数,flags参数是传输控制标志。最后两个参数类似于accept的最后两个参数。

(4)sendto函数:UDP使用sendto()函数发送数据,他类似于标准的write(),但是在sendto()函数中要指明目的地址。

-------------------------------------------------------------------#include#includesize_t

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

struct sockaddr * to, int addrlen);返回发送数据的长度---成功-1---失败-------------------------------------------------------------------

前三个参数等同于函数read()的前三个参数,flags参数是传输控制标志。参数to指明数据将发往的协议地址,他的大小由addrlen参数来指定。

2、修改UDPServer和UDPClient程序,设计一个简单的UDP通信程序,并达到以下要求:

(1)双方能相互发送数据,并显示接收到的数据。

(2)当收到对方的数据为“bye”时,能退出程序。

首先运行服务器端程序server,再运行客户端程序client并发送消息,继续发送一条消息。

在服务器程序关闭的情况,UDP仍然可以发送消息,但是不能确保送达目的地。

运行结果:

a4c26d1e5885305701be709a3d33442f.png Sever和Client进行通讯

3、编程验证实验思考题中问题。

(1)如红色区域所示,sendto()函数肯定会先执行,因为recvfrom()函数没有接收到来自服务器端的消息,返回值会小于0,所以recvfrom()函数不会首先执行。

a4c26d1e5885305701be709a3d33442f.png sendto()优先运行

4、服务器同多个客户端通信。

运行结果如下:经过测试,客户端发给主机的消息保存在消息队列中,例如一号客户端发送“一号”,二号客户端发送“二号”,然后三号客户端发送“三号”,这样服务器会接收这样的消息:一号、二号、三号。

a4c26d1e5885305701be709a3d33442f.png服务器和多个客户端通讯的消息顺序

5 思考题

1、能否在接收数据之间不进行bind()调用?如果能,请说明可能的情况。

可以不进行调用,但是前提是先调用sendto()函数,这样系统会自行进行套接字绑定,可以不进行bind()调用。如果先调用recvfrom()函数的话就必须进行bind绑定。

2、能否使用connect()连接对方?为什么?

UDP中可以使用connect系统调用,UDP中connect操作与TCP中connect操作有着本质区别,TCP中调用connect会引起三次握手,client与server建立连结,UDP中调用connect内核仅仅把对端ip和port记录下来,UDP中可以多次调用connect,TCP只能调用一次connect。

3、能否在不调用sendto()函数之前调用recvfom()函数。

服务端可以在不调用sendto()函数之前调用recvfom()函数,因为服务端要在接受到客户端数据后才发送数据到客户端,但是客户端必须先调用sendto函数后才能调用recvfom函数,因为,当没有客户端发来数据时,服务端一直处于监听状态,客户要先调用sendto函数才能让服务端不处于阻塞模式,然后再调用recvfom接受服务端发来的数据。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

#include#include#include#pragmacomment(lib,"wsock32.lib")usingnamespacestd;intmain()

{intcount;

WSADATA wsaData;

WSAStartup(MAKEWORD(2,2), &wsaData);// 创建套节字SOCKET s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if(s == INVALID_SOCKET)

{

printf("Failed socket() %d \n", ::WSAGetLastError());return0;

}// 也可以在这里调用bind函数绑定一个本地地址,否则系统将会自动安排// 填写远程地址信息 sockaddr_in addr;sockaddr_in addr;

addr.sin_family = AF_INET;

addr.sin_port = htons(4567);intnLen =sizeof(addr);// 注意,这里要填写服务器程序所在机器的IP地址,如果你的计算机没有联网,直接使用127.0.0.1即可addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");// 发送数据charreceivebuff[1024];charsendbuffer[1024];

printf("-----------------我是客户端-------------------------:\n");while(TRUE)

{intnRecv = ::recvfrom(s, receivebuff,1024,0, (sockaddr *)&addr, &nLen);if(nRecv >0)

{//接收数据system("color 0E");

receivebuff[nRecv] ='\0';

printf("从服务器接收到信息 >: %s\n", receivebuff);if(strcmp(receivebuff,"bye") ==0)

{

::closesocket(s);

WSACleanup();return0;

}

}//发送数据//scanf("%s", sendbuffer);gets(sendbuffer);

::sendto(s, sendbuffer, strlen(sendbuffer),0, (sockaddr *)&addr,sizeof(addr));

system("color 5A");

printf("客户端发送信息: %s\n", ++count, sendbuffer);if(strcmp(sendbuffer,"bye") ==0)

{

::closesocket(s);return0;

}

}return0;

}#include#include#include#include#pragmacomment(lib,"wsock32.lib")usingnamespacestd;intmain()

{intcount;

WSADATA wsaData;

WSAStartup(MAKEWORD(2,2), &wsaData);// 创建套节字SOCKET s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if(s == INVALID_SOCKET)

{

printf("Failed socket() \n");return0;

}// 填充sockaddr_in结构sockaddr_in sin;

sin.sin_family = AF_INET;

sin.sin_port = htons(4567);

sin.sin_addr.S_un.S_addr = INADDR_ANY;// 绑定这个套节字到一个本地地址if(::bind(s, (LPSOCKADDR)&sin,sizeof(sin)) == SOCKET_ERROR)

{

printf("Failed bind() \n");return0;

}// 接收数据charreceivebuff[1024];charsendbuff[1024];

sockaddr_in addr;intnLen =sizeof(addr);

printf("-----------------我是服务器-------------------------:\n");while(TRUE)

{intnRecv = ::recvfrom(s, receivebuff,1024,0, (sockaddr *)&addr, &nLen);if(nRecv >0)

{

system("color 0E");

receivebuff[nRecv] ='\0';

printf("从客户端接收到信息 >: %s\n", receivebuff);if(strcmp(receivebuff,"bye") ==0)

{

::closesocket(s);return0;

}// scanf("%s", sendbuff);}

gets(sendbuff);

::sendto(s, sendbuff, strlen(sendbuff),0, (sockaddr *)&addr,sizeof(addr));

system("color 0F");

printf("服务器发送信息: %s\n", ++count, sendbuff);if(strcmp(sendbuff,"bye") ==0)

{

::closesocket(s);

WSACleanup();return0;

}

}return0;

}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

udp通信 c 语言,Windows程序设计 | 基于UDP的C/S通讯实现 的相关文章

  • Kafka遇到30042ms has passed since batch creation plus linger time at org.apache.kafka.clients.producer...

    问题描述 xff1a 运行生产者线程的时候显示如下错误信息 xff1a Expiring 1 record s for XXX 0 30042 ms has passed since batch creation plus linger t
  • tcpdump -w 和 -r 的使用

    tcpdump的说明文档是这样的 xff1a w 将原始的信息包写入 形式如 tcpdump w tmp result txt 我今天试了一下 xff0c 发现其写成的文件如果用cat vim来查看的话 xff0c 都显示为乱码 经过man
  • 如何用Go访问深层嵌套的JSON数据?

    原文来自https hashnode com post how 大多数情况下 xff0c 开发人员需要使用来自其他服务的JSON数据并对其进行查询 查询JSON文档非常耗时 在过去的几天里 xff0c 我正在为Golang编写一个包 xff
  • Identity Card

    Identity Card Time Limit 2000 1000 MS Java Others Memory Limit 32768 32768 K Java Others Total Submission s 995 Accepted
  • 华为S5700交换机开启WEB配置

    近来很多朋友问关于S5700开启WEB不成功的问题 xff0c 现整理出具体步骤和命令 提示 xff1a 华为交换机配置时 xff0c 输入命令前几个字母 xff0c 按TAB可以自动补全命令 xff0c 比如在系统视图下输入sh按下TAB
  • 托管堆上对象的大小(Size)和Layout

    前几天 xff0c 我介绍了托管环境下struct实例的Layout和Size xff0c 其中介绍了StructLayoutAttribute特性 xff0c 其实StructLayoutAttribute特性不只可以用在struct上
  • 网上十大黑客软件大曝光

    网上十大黑客软件大曝光 Internet网上的黑客网站多如牛毛 xff0c 黑客软件也越来越多 越来越黑 笔者现将这些黑客软件分门别类地曝一曝光 xff0c 并提出相应的解决方案 xff0c 以防患于未然 一 古老的WinNuke 平台 x
  • C# DataSet和DataTable详解[转]

    1 创建DataSet对象 xff1a DataSet ds 61 new DataSet 34 DataSetName 34 2 查看调用SqlDataAdapter Fill创建的结构 da Fill ds 34 Orders 34 D
  • 【Python】爬取网站图片

    import requests import bs4 import urllib request import urllib import os hdr 61 39 User Agent 39 39 Mozilla 5 0 X11 Linu
  • 纯C++实现的HTTP请求(POST/GET)

    纯C 43 43 实现的HTTP请求 xff08 POST GET xff09 xff0c 支持windows和linux xff0c 进行简单的封装 xff0c 方便调用 实现如下 xff1a include 34 HttpConnect
  • python subprocess执行shell命令

    2019独角兽企业重金招聘Python工程师标准 gt gt gt subprocess的目的就是启动一个新的进程并且与之通信 subprocess模块中只定义了一个类 Popen 可以使用Popen来创建进程 xff0c 并与进程进行复杂
  • HDTV片源

    微软的 点击进入 苹果 quicktime下载 点击进入 国内一个网站 不过收费 电击进入 还有思路 转载于 https blog 51cto com wangjian 1420
  • Onvif鉴权实现方式

    Onvif鉴权实现方式 Digest 61 B64ENCODE SHA1 B64DECODE Nonce 43 Date 43 Password gsoap中digest生成代码 xff1a int soap wsse add Userna
  • 一文搞定Java并发编程面试考点

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 1 在java中守护线程和本地线程区别 xff1f java中的线程分为两种 xff1a 守护线程 xff08 Daemon xff09 和用户线程 xff08 User
  • 数据结构c语言版创建链表实验报告,C语言数据结构-创建链表的四种方法

    结点类型 xff1a typedef int datatype typedef struct NODE datatype data struct NODE next Node LinkList 1 不带头结点的头插入法创建链表 每创建一个结
  • syslog-ng

    syslog ng配置 March 13th 2012 绚丽也尘埃 Leave a comment Go to comments syslog ng的配置非常简单直观 xff0c 于是乎配置好看看怎么用它实时收集日志 有两台服务器 xff0
  • CMAKE总结(1) .lib .dll .a .so libx.dll libx.dll.a

    2019独角兽企业重金招聘Python工程师标准 gt gt gt win 对应 linux lib a dll 注 so 注 xff1a win中若使用 dll需要一个附带的引入库 lib win gcc中若使用 dll需要一个附带的因入
  • curl命令详解

    cURL 1 手册页 名称 cURL transfer a URL 摘要 cURL 选项 URL 描述 cURL 是一个向服务器或从服务器传输数据的工具 xff0c 它支持HTTP HTTPS FTP FTPS SCP SFTP TFTP
  • curl命令常见用法汇总 good

    curl是一种命令行工具 xff0c 作用是发出网络请求 xff0c 然后得到和提取数据 xff0c 显示在 34 标准输出 34 xff08 stdout xff09 上面 curl是一个强大的命令行工具 xff0c 它可以通过网络将信息
  • Linux编程之select

    select系统调用的的用途是 xff1a 在一段指定的时间内 xff0c 监听用户感兴趣的文件描述符上可读 可写和异常等事件 select 机制的优势 为什么会出现select模型 xff1f 先看一下下面的这句代码 xff1a int

随机推荐