这篇博客的目的是想实现一个简单的UDP服务器程序,完成客户端与服务器端的通信。
因为涉及的小知识点比较多,所以本篇博客的篇幅较长,但是会讲的很详细。
在下一篇博客里,我会总结Linux中用socket实现TCP网络程序
1.程序的第一步是创建套接字(socket)
#include //头文件
//创建套接字函数,socket
int socket(int domain, int type, int protocol);
//domain:地址域,常用的有以下两种
// AF_INET IPv4 Internet protocols ip(7)
// AF_INET6 IPv6 Internet protocols ipv6(7)
//type:套接字类型,常用的有以下两种
// SOCK_STREAM 流式套接字
// SOCK_DGRAM 数据报套接字
//protocol:协议类型
// 默认为0,流式套接字默认TCP协议,数据包套接字默认UDP协议
// 在这里我们直接用UDP协议,即 IPPROTO_UDP
//返回值:int型,成功返回套接字描述符,失败返回-1
2.程序的第二步是为套接字绑定地址信息,确定socket能够操作缓冲区。
首先我们要提到一个网络地址结构体,sockaddr_in.
struct sockaddr_in {
sa_family_t sin_family; /*协议类型: AF_INET */
in_port_t sin_port; /* 网络字节序 */
struct in_addr sin_addr; /*网络地址*/
};
然后就是为套接字绑定地址信息的函数,bind.
#include //头文件
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
// sockfd:套接字描述符
// addr:要绑定的地址信息
// addrlen:地址信息的长度
// 返回值:int型,成功返回0,失败返回-1
3.程序的第三步是接收数据
#include //头文件
#include
ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr *src_addr,socklen_t *addrlen)
// sockfd:套接字描述符
// buf:用于存储接受的数据,把数据拷贝到buf当中
// len:想要接受的数据长度
// flags:默认为0,如果缓冲区没有数据,就阻塞等待直到有数据并拷贝完毕
// src_addr:用于确定数据是哪一个客户端发送的,即确定发送端的地址信息
// addrlen:发送端地址信息的长度,不能为0
// 返回值:ssize_t类型, 成功返回实际接收数据的长度,失败返回-1
4.程序的第四步是发送数据
#include //头文件
#include
ssize_t sendto(int sockfd,const void *buf,size_t len,int flags,const struct sockaddr *dest_addr,socklen_t addrlen);
// sockfd:socket描述符,发送数据的时候就是通过这个socket所绑定的地址来发送
// buf:要发送的数据
// len: 要发送数据的长度
// flags:0-默认阻塞式发送
// dest_addr:数据要发送的目的地址,对端地址
// addrlen:地址信息长度
// 返回值:ssize_t类型,返回实际发送的数据长度,失败返回-1
5.程序的第五步是关闭socket,即close(sockfd)
上面五个步骤是服务器端的步骤,客户端的步骤类似,我不做讲解,下面我把代码分享给大家。
服务器端:udp_client.c
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9
10 int main()
11 {
12 int sockfd=socket(AF_INET,SOCK_DGRAM,0);
13 if(sockfd<0)
14 {
15 perror("socket error\n");
16 return -1;
17 }
18 struct sockaddr_in server;
19 server.sin_family = AF_INET;
20 server.sin_port=htons(9000);
21 server.sin_addr.s_addr=inet_addr("192.168.1.104");
22
23 char buff[1024]={0};
24 struct sockaddr_in peer;
25 while(1)
26 {
27 socklen_t len=sizeof(peer);
28 printf("please enter:");
29 fflush(stdout);
30 ssize_t s=read(0,buff,sizeof(buff)-1);
31 if(s>0)
32 {
33 buff[s-1]=0;
34 sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&server,sizeof(server));
35 ssize_t _s=recvfrom(sockfd,buff,sizeof(buff)-1,0,(struct sockaddr*)&peer,&len);
36 if(_s>0)
37 {
38 buff[_s]=0;
39 printf("Server echo#%s\n",buff);
40 }
41 }
42 }
43
44
45 return 0;
46 }
客户端:udp_server.c
1 //这是一个非常简单的udp服务端程序
2 //功能是:客户端与服务端的聊天程序
3
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12
13 int main()
14 {
15 //1.创建套接字
16 //int socket(int domain, int type, int protocol);
17 int sockfd =socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
18 if(sockfd <0)
19 {
20 perror("socket error\n");
21 return -1;
22 }
23 //2.为套接字绑定地址信息
24 // int bind(int sockfd, const struct sockaddr *addr,
25 // socklen_t addrlen)
26 struct sockaddr_in addr;
27 addr.sin_family=AF_INET;
28 addr.sin_port=htons(9000);
29 addr.sin_addr.s_addr=inet_addr("192.168.1.104");
30 socklen_t addrlen =sizeof(struct sockaddr_in);
31 int ret=bind(sockfd,(struct sockaddr*)&addr,addrlen);
32 if(ret<0)
33 {
34 perror("bind error\n");
35 close(sockfd);
36 return -1;
37 }
38 //3.接收数据
39 // ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
40 // struct sockaddr *src_addr, socklen_t *addrlen);
41 // sockfd: socket描述符
42 // buf: 用于将存储接收的数据
43 // len: 想要接收的数据长度
44 // flags: 0-默认是说如果缓冲区没有数据,那么我就阻塞等待
45 // src_addr: 用于确定数据的发送端地址信息
46 // addrlen: 地址信息的长度
47 // 返回值:实际接收的数据长度 ,-1:失败
48 while(1)
49 {
50 //接收数据
51 char buff[1024]={0};
52 struct sockaddr_in cli_addr;
53 addrlen=sizeof(struct sockaddr_in);
54 ssize_t rlen=recvfrom(sockfd,buff,1023,0,(struct sockaddr*)&cli_addr,&addrlen);
55 if(rlen<0)
56 {
57 perror("recvfrom error\n");
58 close(sockfd);
59 return -1;
60 }
61 //inet_ntoa:将网络地址转换成“.”点隔的字符串格式
62 //ntohs:将网络字节序转成端口字节序。
63 printf("client[%s:%d] say:%s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buff);
64 //发送数据
65 //ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
66 // const struct sockaddr *dest_addr, socklen_t addrlen);
67 //sockfd:socket描述符,发送数据的时候就是通过这个socket所绑定的地址来发送。
68 //buf:要发送的数据
69 //len:要发送数据的长度
70 //flag:0-默认阻塞式发送
71 //dest_addr:数据要发送到的对端地址,目的地址
72 //addrlen:地址信息长度
73 //返回值:返回时记得发送长度,失败返回-1/
74 memset(buff,0x00,1024);//将数组初始化为0
75 printf("server say:");
76 scanf("%s",buff);
77 sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&cli_addr,addrlen);
78
79 }
80 close(sockfd);
81 return 0;
82 }