UDP通信

1.TCP和UDP通信优缺点

img

2.UDP的通信流程

1.serve和client

相对TCP来说, 服务端的accept()和客户端connect()不需要了

recv()和send()只能用于TCP

serve端

  1. lfd = socket(AF_INET,SOCK_DGRAM,0) 由SOCK_STREAM 改为了 SOCK_DGRAM
  2. bind(lfd,地址结构,地址结构大小)
  3. listen() – 可有可无
  4. while(1){ read(cfd,buf,sizeof)–>被替换为–>recvfrom() write()–>被替换为–>sendto() }
  5. close()

client端

  1. confd = socket(AF_INET,SOCK_DGRAM,0)
  2. sendto(服务器的地址结构,地址结构大小)
  3. recvfrom()读回新数据
  4. close()

2.recvfrom和sendto

1
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr,  socklen_t *addrlen);

涵盖accept传出地址结构

参数:

  • sockfd: 套接字

  • buf:缓冲区地址

  • len:缓冲区大小

  • flags: 0

  • src_addr:(struct sockaddr *)&addr 传出。 对端的地址结构

  • addrlen:传入传出,结构体的长度

返回值:

成功接收数据字节数。 失败:-1 errn。 0: 对端关闭

1
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr * dest_addr, socklen_t addrlen);

涵盖connect函数功能,但不会建立连接

参数:

  • sockfd: 套接字

  • buf:存储数据的缓冲区

  • len:数据长度

  • flags: 0

  • src_addr:(struct sockaddr *)&addr 传入。 目标地址结构

  • addrlen:地址结构长度。

返回值:成功写出数据字节数。

失败 -1 并设置error

3.代码实现

serve.c

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
//serve.c
#include"wrap.h"
#define SERV_PORT 9527

int main(int argc,char * argv[])
{
struct sockaddr_in serv_addr,clie_addr;
socklen_t clie_addr_len;
int sockfd;
char buf[4096];
char str[INET_ADDRSTRLEN];
int i,n;
//用于通信的套接字
sockfd=Socket(AF_INET,SOCK_DGRAM,0);
//初始化服务器地址结构
bzero(&serv_addr,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(SERV_PORT);
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//绑定服务器地址连接
Bind(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));

printf("Accepting connections ...\n");

while(1){
clie_addr_len=sizeof(clie_addr);
//代替的是TCP服务器中的accept函数和read函数 会将客户端地址结构传出
n=recvfrom(sockfd,buf,4096,0,(struct sockaddr *)&clie_addr,&clie_addr_len);
printf("receive from:%s port:%d\n",
inet_ntop(AF_INET,&clie_addr.sin_addr.s_addr,str,sizeof(str)),
ntohs(clie_addr.sin_port));
//正常读写
for(i=0;i<n;i++)
buf[i]=toupper(buf[i]);
//代替write函数 通过传入客户端地址结构才能知道客户端的ip和地址 而客户端的sendto不需要 因为server的地址固定 初始化的时候可以直接写上服务器对应的ip和端口号
n=sendto(sockfd,buf,n,0,(struct sockaddr *)&clie_addr,sizeof(clie_addr));
if(n==-1)
perror("sendto error");
}
close(sockfd);
return 0;
}

client.c

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
//client.c
#include"warap.h"
#define SERV_PORT 9527

int main(int argc,char * argv[])
{
int sockfd,n;
char buf[4096];
struct sockaddr_in serv_addr;
sockfd=Socket(AF_INET,SOCK_DGRAM,0);
//初始化地址结构
bzero(&serv_addr,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(SERV_PORT);
inet_pton(AF_INET,"127.0.0.1",&serv_addr.sin_addr);
//bind(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));系统会进行隐式绑定,不需要bind函数,即使加上它也不会有实际的效果,还是按照系统的来
while(fgets(buf,4096,stdin)!=NULL){
//相当于client的connect函数
n=sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
if(n==-1)
perror("sendto error");
//相当于read函数
n=recvfrom(sockfd,buf,4096,0,NULL,0);
if(n==-1)
perror("recvfrom error");
//写到屏幕上
Write(STDOUT_FILENO,buf,n);
}
Close(sockfd);
return 0;
}

warp.c wrap.h

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
//wrap.h
#ifndef __WRAP_H_
#define __WRAP_H_

#include<sys/epoll.h>
#include<sys/select.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<string.h>
#include<dirent.h>
#include<sys/stat.h>
#include<wait.h>
#include<sys/mman.h>
#include<signal.h>
#include<pthread.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<strings.h>
#include<netinet/in.h>

#define SRV_PORT 9527


void perr_exit(const char *s);
int Accept(int fd,struct sockaddr *sa,socklen_t * salenptr);
int Bind(int fd, const struct sockaddr *sa, socklen_t salen);
int Connect(int fd, const struct sockaddr *sa, socklen_t addrlen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
size_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd,const void *ptr,size_t nbytes);
int Close(int fd);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
ssize_t my_read(int fd, char *ptr);
ssize_t Readline(int fd, void *vptr, size_t maxlen);



#endif

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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//wrap.c
#include"warp.h"


void perr_exit(const char *s)
{
perror(s);
exit(1);
}

int Accept(int fd,struct sockaddr *sa,socklen_t * salenptr)
{
int n;
again:
if((n=accept(fd,sa,salenptr))<0)
{
if((errno==ECONNABORTED)||(errno==EINTR))
goto again;
else
perr_exit("accept error");
}
return n;
}

int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
int n;
if((n=bind(fd,sa,salen))<0)
perr_exit("bind error");

return n;
}

int Connect(int fd, const struct sockaddr *sa, socklen_t addrlen)
{
int n;
n=connect(fd,sa,addrlen);
if(n<0)
{
perr_exit("connect error");
}
return n;
}

int Listen(int fd, int backlog)
{
int n;
if((n=listen(fd,backlog))<0)
perr_exit("listen error");
return n;
}

int Socket(int family, int type, int protocol)
{
int n;
if((n=socket(family,type,protocol))<0)
perr_exit("socket error");
return n;
}

size_t Read(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
again:
if((n=read(fd,ptr,nbytes))==-1)
{
if(errno==EINTR)
goto again;
else
return -1;
}
return n;
}

ssize_t Write(int fd,const void *ptr,size_t nbytes)
{
ssize_t n;
again:
if((n=write(fd,ptr,nbytes))==-1)
{
if(errno==EINTR)
goto again;
else
return -1;
}

return 0;
}

int Close(int fd)
{
int n;
if((n=close(fd))==-1)
perr_exit("close error");

return n;
}

ssize_t Readn(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;

ptr=vptr;
nleft=n;

while(nleft>0)
{
if((nread=read(fd,ptr,nleft))<0)
{
if(errno==EINTR)
nread=0;
else
return -1;
}
else if(nread==0)
break;

}
}
ssize_t Writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
char *ptr;

ptr=vptr;
nleft=n;

while(nleft>0)
{
if((nwritten=write(fd,ptr,nleft))<=0)
{
if(nwritten<0&&errno==EINTR)
nwritten=0;
else
return -1;
}
nleft-=nwritten;
ptr+=nwritten;
}
return n;
}

ssize_t my_read(int fd, char *ptr)
{
static int read_cnt;
static char *read_ptr;
static char read_buf[100];

if(read_cnt<=0)
{
again:
if((read_cnt=read(fd,read_buf,sizeof(read_buf)))<0)
{
if(errno==EINTR)
goto again;
return -1;
}else if(read_cnt==0)
return 0;
read_ptr=read_buf;
}
read_cnt--;
*ptr=*read_ptr++;
return 1;
}

ssize_t Readline(int fd, void *vptr, size_t maxlen)
{
ssize_t n,rc;
char c,*ptr;
ptr=vptr;

for(n=1;n<maxlen;n++)
{
if((rc=my_read(fd,&c))==1)
{
*ptr++=c;
if(c=='\n')
break;
}else if(rc==0)
{
*ptr=0;
return n-1;
}
else
return -1;
}
*ptr=0;
return n;
}