MFC]UDP通信的简单实现
1. UDP和TCP最⼤的区别:
1) TCP最⼤的特点就是⾯向连接、安全可靠,也就是说TCP通信必须要先建⽴连接,并且通信过程需要时时校验,如果数据有误需要重发; 2) UDP最⼤的特点就是⾯向⽆连接,不可靠,也就是说不⽤建⽴连接就直接向⽬标发送信息,并且通信过程中不做任何校验,如果数据丢失或者有误也不管;
3) 听上去UDP⾮常的⽆⽤,但其实不然,UDP最⼤的优势就是速度快,⽽TCP在连接和校验的过程中会消耗⾮常多的时间,因此TCP ⼀般⽤于对数据要求精确⽆误的场合下,⽐如下载程序(迅雷等),可想⽽知,若你下载⼀个软件,中间传输的数据有误那软件岂不是⽤不了了吗?
4) UDP的应⽤场合通常是即时通讯等要求速度⾼于质量的场合,⽐如视频对话、⽹络对话等,在这种场合下,特别是在视频聊天时,视频质量可以不那么清晰(UDP不对数据校验),但是画⾯必须是时时的,如果⽤TCP的话可能视频声⾳是当前的声⾳,但是画⾯可能还是是⼏秒前的画⾯,这就不符合即时的要求了!!因此UDP的应⽤场合还是⾮常多的!
奥尼尔
2. UDP的Sockets编程:
1) ⾸先最⼤的特点就是客户端不需要使⽤connect连接,服务器端也不需要listen来监听请求,但不过建⽴套接字的过程还是和TCP⼀样的; 2) 服务器端不需要accept了,因为不需要监听,⽽是直接可以⽤recvfrom函数接受客户端发送的数据!
3) ⽽客户端由于不需要connect连接服务器端,因此可以直接使⽤sendto函数向⽬标服务器发送数据;
4) 双⽅都可以直接使⽤sendto和recvfrom进⾏数据通信;
5) UDP套接字的配置:
i. ⾸先需要在socket()函数中指定为SOCK_DGRAM,即数据包套接字类型(基于UDP);
ii. 在TCP中,数据收发必须持有对⽅的套接字,⽽服务器端监听、接收请求必须持有本地的套接字,⼀般需要两个套接字来⽀持;
iii. 但是在UDP中,通信双⽅只能持有⼀个套接字,即都是本地的套接字,发送的时候需要指定对⽅的套接字地址,⽽接收的时候需要⽤⼀个空的套接字地址接收对⽅的地址,即收发时sendto和recvfrom中的套接字句柄s都是绑定了本地地址的套接字,收发统统必须持有⾃⼰的套接字; 钢结构论坛
iv. 也就是说数据收发的缓存都是⽤本地套接字!⽽TCP中数据收发的缓存都是⽤对⽅的套接字(建⽴在本地程序中);
v. 因此,双⽅在收发数据之前必须先对本地地址进⾏绑定,服务器端仍然可以使⽤bind进⾏显⽰的绑定,但是在Winsock⼿册中明确讲了不⽀持在客户端中使⽤bind来显⽰绑定⾃⼰的地址,因为显⽰绑定往往需要你输⼊精确的地址,⽽有些时候地址是动态分配的,每次使⽤的都可能不⼀样,因此不推荐在客户端中显⽰的使⽤bind来绑定⾃⼰的地址;
vi. 还好,sendto函数在第⼀次调⽤的时候就能隐式地绑定当前的地址,由于服务器端只能被动地等待请求,因此不可能⽐recvfrom 先调⽤sendto,所以服务器端要先使⽤bind来绑定本地地址,⽽客户端必须主动请求向服务器端发送信息,因此不肯能⽐sendto先调⽤recvfrom,因此sendto⼀定先调⽤,⽽调⽤的同时也⾃动绑定了本地地址了;
6) sendto:
i. 函数原型:
[cpp]
1. int sendto(
2. SOCKET s, // 绑定中本地地址的套接字
3. const char FAR* buf, // 发送数据的缓存
4. int len, // 数据的长度(字节)
5. int flags, // 函数调⽤模式,⼀般为0
6. const struct sockaddr FAR* to, // ⽬标地址
7. int tolen, // ⽬标地址结构的⼤⼩
8. );
int sendto(
SOCKET s, // 绑定中本地地址的套接字
const char FAR* buf, // 发送数据的缓存
int len, // 数据的长度(字节)
int flags, // 函数调⽤模式,⼀般为0
const struct sockaddr FAR* to, // ⽬标地址
int tolen, // ⽬标地址结构的⼤⼩
);
ii. 该函数将返回实际发送的字节数,当然可能⼩于指定的字节数,如果失败则会返回相应的错误码;
7) recvfrom:
i. 函数原型:
[cpp]
1. int recvfrom(
2. SOCKET s, // 绑定本地地址的套接字
3. char FAR* buf, // 接受数据的缓存
4. int len, // 接受多少字节
5. int flags, // ⼀般为0
6. struct sockaddr FAR* from, //⽤于接受数据源的地址
7. int FAR* fromlen // 数据源的地址的⼤⼩(字节)
8. );
int recvfrom(
SOCKET s, // 绑定本地地址的套接字
char FAR* buf, // 接受数据的缓存
int len, // 接受多少字节
int flags, // ⼀般为0
struct sockaddr FAR* from, //⽤于接受数据源的地址
int FAR* fromlen // 数据源的地址的⼤⼩(字节)
);
ii. 该函数将返回实际收到的字节数,如果套接字被正常关闭将返回0,否则将返回错误码;
!!下⾯将演⽰⼀个简单的UDP通信实例,实现的内容和上⼀个TCP通信实例⼀样;
服务器端:
[cpp]
1. #include <winsock
2.h>
2. #include <windows.h>
3. #include <stdio.h>
4.
5. #pragma comment(lib, "ws2_32.lib")
6.
7. int main() {
8.
9. static const char szAnswerClient[] = "Hello! You've been connected!";
10. char szBuff[50] = { 0 };
11.
12. WSADATA data;
注射执行死刑13. WORD wVersionRequired = MAKEWORD(2, 0);
14. WSAStartup(wVersionRequired, &data);
15.
16. SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
17. sockaddr_in addr;
18. addr.sin_family = AF_INET;
19. addr.sin_port = htons(75);
20. addr.sin_addr.S_un.S_addr = INADDR_ANY;
21. bind(s, (sockaddr*)&addr, sizeof(addr));
22.
23. printf("Server is setup and now waiting for clients' \n");
24.
25. sockaddr_in addrClient;
26. int nSockAddrSize = sizeof(addrClient);
27. if (recvfrom(s, szBuff, sizeof(szBuff), 0, (sockaddr*)&addrClient, &nSockAddrSize) > 0) {
28. printf("There is one client(%s) connected!\n", inet_ntoa(addrClient.sin_addr));
29. printf("%s\n", szBuff);
30. sendto(s, szAnswerClient, sizeof(szAnswerClient), 0, (sockaddr*)&addrClient, nSockAddrSize);
31. }
32.
33. closesocket(s);
34. WSACleanup();
35.
36. if (getchar()) return 0;
37. return 0;
38. }
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
战斗机代数划分
int main() {
static const char szAnswerClient[] = "Hello! You've been connected!";
char szBuff[50] = { 0 };
WSADATA data;
失效分析与预防
WORD wVersionRequired = MAKEWORD(2, 0);
WSAStartup(wVersionRequired, &data);
SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(75);
addr.sin_addr.S_un.S_addr = INADDR_ANY;
bind(s, (sockaddr*)&addr, sizeof(addr));
printf("Server is setup and now waiting for clients' \n");
sockaddr_in addrClient;
int nSockAddrSize = sizeof(addrClient);
if (recvfrom(s, szBuff, sizeof(szBuff), 0, (sockaddr*)&addrClient, &nSockAddrSize) > 0) {
printf("There is one client(%s) connected!\n", inet_ntoa(addrClient.sin_addr));
printf("%s\n", szBuff);
sendto(s, szAnswerClient, sizeof(szAnswerClient), 0, (sockaddr*)&addrClient, nSockAddrSize); }
closesocket(s);
WSACleanup();
if (getchar()) return 0;
return 0;
}
客户端:
[cpp]
1. #include <winsock
2.h>
2. #include <windows.h>
3. #include <stdio.h>
4.
5. #pragma comment(lib, "ws2_32.lib")
6.
7. int main() {
8.
9. static const char szSendToServer[] = "Hello! I'm trying to connect you!";
10. char szBuff[50] = { 0 };
11.
12. WSADATA data;
13. WORD wVersionRequested = MAKEWORD(2, 0);
14. WSAStartup(wVersionRequested, &data);
15.
16. SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
17. sockaddr_in addr;
18. addr.sin_family = AF_INET;
19. addr.sin_port = htons(75);
20. addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
21.
22. printf("Client is setup and now trying to \n");
23.
24. sockaddr_in addrServer;
25. int nSockAddrSize = sizeof(addrServer);
26. sendto(s, szSendToServer, sizeof(szSendToServer), 0, (sockaddr*)&addr, nSockAddrSize);
27. recvfrom(s, szBuff, sizeof(szBuff), 0, (sockaddr*)&addrServer, &nSockAddrSize);
28. printf("%s\n", szBuff);
29.
30. closesocket(s);
31. WSACleanup();
32.
33. if (getchar()) return 0;
34. return 0;
35. }
阿伊努人