根据UDP协议的特性,我们实现一个简单的回显服务器。
UDP协议:
传输层协议
无连接
不可靠
面向数据报
服务器代码:
///////////////
// UDP版本的服务器
// 1.绑定端口号
// 2.服务器7*24小时等待客户端发送的请求
// 3.把响应发回客服端
//////////////
#include
#include
#include
#include
#include
#include
#include
void service(int sock)
{
char buf[128];
for(;;)
{
//临时的sockaddr_in 结构体接收
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
buf[0] = 0;
//接收客户端发来的数据 数据存在buf
ssize_t s = recvfrom(sock,buf,sizeof(buf)-1,\
0,(struct sockaddr*)&peer,&len);
if(s > 0)
{
buf[s] = 0;
//打印从哪个客户端接收到的数据
printf("[%s,%d] # %s\n",inet_ntoa(peer.sin_addr),\
ntohs(peer.sin_port),buf);
//把响应发送给客户端 buf
sendto(sock,buf,strlen(buf),0,\
(struct sockaddr*)&peer,len);
}
}
}
// ./server 0 8080
int main(int arg,char *argv[])
{
if(arg != 3)
{
printf("usage: %s [ip] [port]\n",argv[0]);
return 1;
}
//SOCK_DGRAM表示UDP ipv4: AF_INETET
int sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock < 0)
{
printf("socket error!\n");
return 2;
}
//初始化sockaddr_in local IPV4: AF_INET
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(atoi(argv[2]));
//字符串IP地址 转换成 in_addr_t :inet_addr() 看博主博客的地址转换函数的介绍
local.sin_addr.s_addr = inet_addr(argv[1]);
//绑定端口号
if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
printf("bind error\n");
return 3;
}
//业务 --- 回显
service(sock);
close(sock);
return 0;
}
客户端代码:
// UDP 版本的客户端
// 1. 从标准输入, 读入一个字符串
// 2. 把这个字符串通过 socket 发送给服务器
// 3. 尝试从服务器读取响应数据
// 4. 把响应结果写到标准输出上.
///////////////////////////////////////////////////////
#include
#include
#include
#include
#include
#include
#include
//client 127.0.0.1 8080
int main(int arg,char *argv[])
{
if(arg != 3)
{
printf("usage: %s [ip] [port]\n",argv[0]);
return 1;
}
//创建sock < 0 创建失败 SOCK_DGRAM表示UDP ipv4: AF_INETET
int sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock < 0)
{
printf("socket error\n");
return 2;
//初始化 sockaddr_in ipv4: AF_INETET
struct sockaddr_in server;
server.sin_family = AF_INET;
//端口号:atoi 字符转换成数字
server.sin_port = htons(atoi(argv[2]));
//IP地址转换函数 ----看博主博客地址转换函数的介绍
server.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t len = sizeof(server);
//缓冲区
char buf[128];
//回显服务器,死循环
for(;;)
{
buf[128] = 0;
//从标准输入中读取存放在buf sizeof(buf)-1 是为了最后为一个字节为0
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s-1] = 0;
//发送数据
sendto(sock,buf,strlen(buf),0,(struct sockaddr*)&server,len);
//接受数据
s = recvfrom(sock,buf,sizeof(buf)-1,0,NULL,NULL);
if(s > 0)
{
buf[s] = 0;
printf("server echo# %s\n",buf);
}
}
}
close(sock);
return 0;
}
ps:
发送函数:
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd: socket
const void *buf: 缓冲区地址
size_t len:缓冲区的大小,buf的大小
int flags: 0
const struct sockaddr *dest_addr,结构体的地址
socklen_t addrlen : 结构体大小
接收函数:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
int sockfd:socket
void *buf:缓冲区地址
size_t len:缓冲区大小
int flags:0
struct sockaddr *src_addr:结构体地址
socklen_t *addrlen:结构体大小