目录
UDP 客户端呼叫 和 服务器应答 通信
- 基于C语言实现,UDP练习——用recvfrom( )/ sendto( )
- 命令行输入指定IP、端口
流程
-
服务器:
1.创建流式套接字 socket
2.填充服务器的网络信息结构体
3.将网络信息结构体和套接字进行绑定 bind
5.收发数据 readfrom / sendto
6.关闭套接字 close -
客户端:
1.创建流式套接字 socket
2.填充服务器的网络信息结构体
3.收发数据 readfrom / sendto
4.关闭套接字 close
字节序转换函数
- 主机字节序到网络字节序
h host 主机
n net 网络
将无符号4字节整型 从主机-->网络
uint32_t htonl(uint32_t hostlong);
将无符号2字节整型 从主机-->网络
uint16_t htons(uint16_t hostshort);
- 网络字节序到主机字节序
将无符号4字节整型 从网络-->主机
uint32_t ntohl(uint32_t netlong);
将无符号2字节整型 网络-->主机
uint16_t ntohs(uint16_t netshort);
IP地址转换的函数:
- inet_addr()
将strptr所指的字符串转换成32位的网络字节序二进制值。
in_addr_t inet_addr(const char*strptr);
- inet_ntoa()
将32位网络字节序二进制地址转换成点分十进制的字符串。
char *inet_ntoa(stuct in_addr inaddr);
将套接字和网络信息结构体绑定bind
- 网络信息结构体 sockaddr
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
只用于强制类型转换,防止编译器警告
- sockaddr_in / in_addr
struct sockaddr_in {
sa_family_t sin_family; /* AF_INET */
in_port_t sin_port; /* 网络字节序的端口号 */
struct in_addr sin_addr; /* IP地址 */
};
struct in_addr {
uint32_t s_addr; /* 网络字节序的IP地址 */
};
代码实现
服务器----02server.c
#include <stdio.h>
/*socket-bind-listen-accept*/
#include <sys/types.h>
#include <sys/socket.h>
/*memset*/
#include <string.h>
/*sockaddr_in结构体*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
/*inet_addr*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/*close*/
#include <unistd.h>
/*exit*/
#include <stdlib.h>
#define ERRLOG(errmsg) \
do \
{ \
printf("%s--%s(%d):", __FILE__, __func__, __LINE__); \
perror(errmsg); \
exit(-1); \
} while (0)
int main(int argc, const char *argv[])
{
if (3 != argc)
{
printf("Usage : %s <IP> <PORT>\n", argv[0]);
exit(-1);
}
// 1.创建套接字 // IPV4使用,//UDP
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd)
{
ERRLOG("socket error");
}
// 2.填充服务器网络信息结构体
struct sockaddr_in server_addr;
//清空、填充0
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; // IPV4
//端口号 填 8888 9999 6789 ...都可以
//将无符号2字节整型 主机-->网络
server_addr.sin_port = htons(atoi(argv[2]));
// ip地址 要么是当前Ubuntu主机的IP地址 或者
//如果本地测试的化 使用 127.0.0.1 也可以
//将strptr所指的字符串转换成32位的网络字节序二进制值。
server_addr.sin_addr.s_addr = inet_addr(argv[1]);//!!!!!!!!
//结构体长度
socklen_t server_addr_len = sizeof(server_addr);
// 3.将套接字和网络信息结构体绑定//强制类型转换//网络信息结构体
if (-1 == bind(sockfd, (struct sockaddr *)&server_addr, server_addr_len))
ERRLOG("bind error");
//用来保存客户端信息的结构体
// UPD网络通信
//如果给发送端回信,就必须保存发送端的网络信息结构体
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(client_addr));
socklen_t client_addr_len = sizeof(client_addr);
//-------------------------------------------------------------------------------
char buff[128] = {0};
while (1)
{
//阻塞接收发送端发来的数据 //强制类型转换//发送端的网络信息结构体
if (-1 == recvfrom(sockfd, buff, 128, 0, (struct sockaddr *)&client_addr, &client_addr_len))
ERRLOG("recvfrom error");
printf("客户端 (%s:%d) >",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
//!!!!!!!!!!!!!!!
printf("[%s]\n", buff);
//回复
printf("input > ");
memset(buff, 0, sizeof(buff));
scanf("%s", buff);
if (0 == strcmp(buff, "quit"))
break;
if (-1 == sendto(sockfd, buff, 128, 0, (struct sockaddr *)&client_addr, client_addr_len))
ERRLOG("sendto error");
}
close(sockfd);
return 0;
}
客户端----01client.c
#include <stdio.h>
/*socket-bind-listen-accept*/
#include <sys/types.h>
#include <sys/socket.h>
/*memset*/
#include <string.h>
/*sockaddr_in结构体*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
/*inet_addr*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/*close*/
#include <unistd.h>
/*exit*/
#include <stdlib.h>
#define ERRLOG(errmsg) \
do \
{ \
printf("%s--%s(%d):", __FILE__, __func__, __LINE__); \
perror(errmsg); \
exit(-1); \
} while (0)
int main(int argc, const char *argv[])
{
if (3 != argc)
{
printf("Usage : %s <IP> <PORT>\n", argv[0]);
exit(-1);
}
// 1.创建套接字 // IPV4使用,//UDP
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd)
{
ERRLOG("socket error");
}
// 2.填充服务器网络信息结构体
struct sockaddr_in server_addr;
//清空、填充0
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; // IPV4
//端口号 填 8888 9999 6789 ...都可以
//将无符号2字节整型 主机-->网络
server_addr.sin_port = htons(atoi(argv[2]));
// ip地址 要么是当前Ubuntu主机的IP地址 或者
//如果本地测试的化 使用 127.0.0.1 也可以
//将strptr所指的字符串转换成32位的网络字节序二进制值。
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
//结构体长度
socklen_t server_addr_len = sizeof(server_addr);
//-------------------------------------------------------------------------------
char buff[128] = {0};
while (1)
{
printf("input > ");
memset(buff, 0, sizeof(buff));
scanf("%s", buff);
if (0 == strcmp(buff, "quit"))
break;
//将数据发给服务器 //强制类型转换 //网络信息结构体
if (-1 == sendto(sockfd, buff, 128, 0, (struct sockaddr *)&server_addr, server_addr_len))
ERRLOG("sendto error");
//接收服务器的应答
//客户端就无须再保存服务器的网络信息结构体了
//因为server_addr 没有改变
if (-1 == recvfrom(sockfd, buff, 128, 0, NULL, NULL))
ERRLOG("recvfrom error");
//输出应答
printf("服务器 > [%s]\n", buff);
}
close(sockfd);
return 0;
}
执行结果
注意
recvfrom ——从套接字中接收一条消息
//0 阻塞
recvfrom(sockfd, buf, len, 0,
struct sockaddr *src_addr, socklen_t *addrlen);
//保存发送端网络信息的结构体的首地址 //&addrlen!!!!!!
sendto——向套接字中发送一条消息
sendto(sockfd,buf, len,0,
const struct sockaddr *dest_addr, socklen_t addrlen);
//addrlen!!!!!