本文主要是在Linux环境下编写socket程序,本次实现简单UDPecho serverecho client,包含简单分包操作和协议设计

笔记

UDP

用户数据报协议, 不会存在ACK应答消息, 类似于广播的无连接传输层协议.
就是说UDP仅需要一个套接字便可以与多台主机通信.

img
img

基于UDP的I/O函数

img
img
img
img

实现基于UDP的Echo服务器

需求

img
img

Server

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
//
// Created by xmmmmmovo on 2020/4/29.
//
#include <arpa/inet.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/socket.h>
#include <unistd.h>

//constexpr int BUF_SIZE = 256;
constexpr int BUF_SIZE = 10;

void error_handler(char *message);

int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s <port> \n", argv[ 0 ]);
exit(1);
}

int sock = socket(PF_INET, SOCK_DGRAM, 0);

sockaddr_in sockAddr{};
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family = AF_INET;
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
sockAddr.sin_port = htons(atoi(argv[ 1 ]));

if (-1 == bind(sock, (struct sockaddr *) &sockAddr, sizeof(sockAddr))) {
error_handler("bind() error!");
}

sockaddr_in clntAddr{};
socklen_t clntAddrLen = sizeof(clntAddr);

char buf[ 512 ];
while (true) {
int recv_len = recvfrom(sock, buf, BUF_SIZE, 0, (struct sockaddr *) &clntAddr, &clntAddrLen);
printf("recv len = %d\n", recv_len);
sendto(sock, buf, recv_len, 0, (struct sockaddr *) &clntAddr, sizeof(clntAddr));
}

close(sock);
return 0;
}

void error_handler(char *message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(0);
}

Client

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
//
// Created by xmmmmmovo on 2020/4/29.
//
#include <arpa/inet.h>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <sys/socket.h>
#include <unistd.h>

//constexpr int BUF_SIZE = 256;
constexpr int BUF_SIZE = 10;

void error_handler(char *message);

class InetAddress {
public:
explicit InetAddress(std::string port, const std::string ip = "") {
memset(&addr_, 0, sizeof(addr_));
addr_.sin_family = AF_INET;
addr_.sin_port = htons(atoi(port.c_str()));

if (ip == "")
addr_.sin_addr.s_addr = htonl(INADDR_ANY);
else
addr_.sin_addr.s_addr = inet_addr(ip.c_str());
}

~InetAddress() = default;

sockaddr_in &getAddr() {
return addr_;
}

private:
struct sockaddr_in addr_ {};
};

class Socket {
public:
explicit Socket(int type) {
fd_ = socket(PF_INET, type, 0);
}
~Socket() {
close(fd_);
}

ssize_t sendto(InetAddress &addr, void *pData, size_t nBytes) const {
sockaddr *toAddr = (sockaddr *) &(addr.getAddr());
socklen_t addrLen = sizeof(addr.getAddr());

return ::sendto(fd_, pData, nBytes, 0, toAddr, addrLen);
}

ssize_t recvfrom(InetAddress &addr, void *buf, size_t nBytes) const {
return ::recvfrom(fd_, buf, nBytes, 0, 0, 0);
}

private:
int fd_;
};

int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Usage: %s <ip> <port> \n", argv[ 0 ]);
exit(1);
}

Socket sock(SOCK_DGRAM);

InetAddress addr(argv[ 2 ], argv[ 1 ]);

char buf[ 1024 ];
int sl = 0;
while (true) {
fgets(buf + 3, sizeof(buf) - 3, stdin);
sl = strlen(buf + 3) - 1;
printf("str len = %d\n", sl);
if (sl < BUF_SIZE - 3) {
buf[ 1 ] = (char) 1;
buf[ 2 ] = (char) sl;
int send_len = sock.sendto(addr, buf + 1, sl + 2);
printf("send len = %d\n", send_len);
} else {
int pack_num = ceil((double) sl / (BUF_SIZE - 3));
buf[ 0 ] = (char) 0;
buf[ 1 ] = (char) pack_num;
sock.sendto(addr, buf, 2);
int st = 0;
for (int i = 0; i < pack_num; ++i) {
st = i * (BUF_SIZE - 3);
buf[ st ] = (char) 2;
buf[ st + 1 ] = (char) (i + 1);
buf[ st + 2 ] = i == pack_num - 1 ? strlen(buf + st + 3) : BUF_SIZE - 3;
sock.sendto(addr, buf + st, BUF_SIZE);
}
}


int recv_len = sock.recvfrom(addr, buf, BUF_SIZE);
buf[ recv_len ] = 0;

if (buf[ 0 ] == 1) {
printf("Message from server: %s\n", buf + 2);
} else if (buf[ 0 ] == 0) {
// 数据包获取
int pack_num = (int) buf[ 1 ];
int cnt = 0;
char pc_buf[ BUF_SIZE ];
for (int i = 0; i < pack_num; ++i) {
recv_len = sock.recvfrom(addr, pc_buf, BUF_SIZE);
std::copy(pc_buf + 3, pc_buf + recv_len, buf + cnt);
cnt += (int) pc_buf[ 2 ];
}
printf("Message from server: %s\n", buf);
}
}

return 0;
}

void error_handler(char *message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(0);
}

结果

client

img
img

server

img
img