客户端逻辑(不需要绑定端口号)
1.用户输入数据,从标准输入输入一个字符串
2.把这个字符串发送给服务器
3.从服务器读取并返回结果
4.把响应写到标准输出上
服务器逻辑(绑定端口号)
1.启动(初始化)
2.进入死循环(事件循环)
a)从socket中读取请求(Request)
b)根据Request的内容计算生成Response
c)把Response响应写回socket
服务器 server.c
int main(int argc, char* argv[])
{
if(argc != 3)
{
printf("Usage: [ipaddr] [port]\n");
exit(1);
}
//创建socket IPV4 UDP 不关心置0
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
//创建addr_in结构体,并初始化
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2])); //服务器端口号 atoi函数把字符串转换成整型 htons函数把端口号从主机字节序转成网络字节序
addr.sin_addr.s_addr = inet_addr(argv[1]); //ip地址转换 将一个点分十进制的数转化为二进制的网络字节序的IPV4地址
//将服务器ip和端口号进行绑定
if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
{
perror("bind");
exit(2);
}
//申请缓存区收发数据
char buf[1024];
while(1)
{
struct sockaddr_in client; //捕获数据发送源的地址,以便sendto发送
socklen_t clientLen = sizeof(client);
//recvfrom()函数:从套接字端口上接收数据,并解析出数据携带的客户端端口地址信息 ,然后放到定义好的client结构体中,
//参数:sockfd 申请的套接字;buf 收到数据存放的缓存区 ;sizeof(buf)-1 最多存放的大小;
0 操作方式,不关心置0; &clien 指向携带有数据源地址信息的缓冲区(指针); &clientLen 解析过程中clientLen大小可能会发生变化,所以传指针
ssize_t s = recvfrom(sockfd, buf ,sizeof(buf)-1,0,(struct sockaddr*)&client,&clientLen);
if(s > 0)
{
buf[s] ='/0';
//把转化的数据再反转输出
printf("IP: %s , Port : %d : %s\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buf);
//sendto()函数: 向指定目的地发送数据
//参数:同上,这里的client为装有发送数据的目的地址缓存区(指针); clientLen目的地址缓存区长度(整型)
sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&client,clientLen);
}
}
close(sockfd);
return 0;
}
客户端client.c
int main(int argc,char* argv[])
{
if(argc != 3)
{
printf("Usage: [ipaddr] [port]\n");
exit(1);
}
//创建socket IPV4 UDP 指定协议 不关心置0
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
//创建addr_in结构体并初始化
struct sockaddr_in client;
client.sin_family = AF_INET;
client.sin_port = htons(atoi(argv[2]));
client.sin_addr.s_addr = inet_addr(argv[1]);
//建立缓存区,准备向服务器发送数据
printf("Please Enter : \n");
char buf[1024];
while(1)
{
printf("#client : ");
fflush(stdout);
//从标准输入读数据进buf
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s < 0)
{
perror("read");
exit(3);
}
buf[s-1] = '\0';
//sendto()函数: 向指定目的地发送数据
//参数:sockfd 申请的套接字;buf 收到数据存放的缓存区 ;strlen(buf) 已经写在缓存区的长度;
0 操作方式,不关心置0;client为装有发送数据的目的地址缓存区(指针); clientLen目的地址缓存区长度(整型)
s = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&client,sizeof(client));
if(s < 0)
{
perror("sendto");
exit(4);
}
//sendto()函数:创建一个接收对端ip&端口信息的addr_in结构体server
struct sockaddr_in server;
socklen_t len = sizeof(server);
//recvfrom()函数:从套接字端口上接收数据,并解析出客户端的端口地址信息 ,然后放到定义好的client结构体中,
//参数:sockfd 申请的套接字;buf 收到数据存放的缓存区 ;sizeof(buf)-1 最多存放的大小;
0 操作方式,不关心置0; &clien 指向携带有数据源地址信息的缓冲区(指针); &clientLen 解析过程中clientLen大小可能会发生变化,所以传指针
s = recvfrom(sockfd,buf,sizeof(buf)-1,0,(struct sockaddr*)&server,&len);
if(s < 0)
{
perror("recvfrom");
exit(5);
}
//
buf[s] = '/0';
//把转化的数据再反转输出
printf("IP : %s , Port : %d : %s\n",inet_ntoa(server.sin_addr),ntohs(server.sin_port),buf);
}
//关闭
close(sockfd);
return 0;
}