本文翻译自: How to implement UDP sockets in C
UDP不同于TCP,是一种无连接协议,在发送或接收数据之前不需要进行握手操作,这简化了执行。
1.服务器端
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(void)
{
int socket_desc;
struct sockaddr_in server_addr, client_addr;
char server_message[2000], client_message[2000];
int client_struct_length = sizeof(client_addr);
// Clean buffers:
memset(server_message, '\0', sizeof(server_message));
memset(client_message, '\0', sizeof(client_message));
// Create UDP socket:
socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(socket_desc < 0){
printf("Error while creating socket\n");
return -1;
}
printf("Socket created successfully\n");
// Set port and IP:
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(2000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// Bind to the set port and IP:
if(bind(socket_desc, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){
printf("Couldn't bind to the port\n");
return -1;
}
printf("Done with binding\n");
printf("Listening for incoming messages...\n\n");
// Receive client's message:
if (recvfrom(socket_desc, client_message, sizeof(client_message), 0,
(struct sockaddr*)&client_addr, &client_struct_length) < 0){
printf("Couldn't receive\n");
return -1;
}
printf("Received message from IP: %s and port: %i\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
printf("Msg from client: %s\n", client_message);
// Respond to client:
strcpy(server_message, client_message);
if (sendto(socket_desc, server_message, strlen(server_message), 0,
(struct sockaddr*)&client_addr, client_struct_length) < 0){
printf("Can't send\n");
return -1;
}
// Close the socket:
close(socket_desc);
return 0;
}
解释:
需要包括以下两个头文件:
#include <sys/socket.h>
#include <arpa/inet.h>
创建一个套接字,返回一个套接字描述符,这被用于指代码后面的套接字:
int socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
服务端的代码将服务器和客户端的地址信息保存在变量server_addr(一个sockaddr_in类型的结构体变量)。利用端口号和IP地址初始化服务器地址:
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(2000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
将套接字描述符和服务器地址绑定:
bind(socket_desc, (struct sockaddr*)&server_addr, sizeof(server_addr);
不同于TCP,服务端不会等待客户端的连接,因此不会优先接收客户端的地址以便发送和接收数据。服务端使用recvfrom()函数接收数据时,它才会接收客户端的信息:
int client_struct_length = sizeof(client_addr);
recvfrom(socket_desc, client_message, sizeof(client_message), 0,(struct sockaddr*)&client_addr, &client_struct_length);
客户端的信息,存储在结构体sockaddr_in类型的变量client_addr中,然后被用于向同样的客户端发送数据:
sendto(socket_desc, server_message, strlen(server_message), 0,(struct sockaddr*)&client_addr, client_struct_length);
关闭套接字以结束通信
close(socket_desc);
2.客户端
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(void)
{
int socket_desc;
struct sockaddr_in server_addr;
char server_message[2000], client_message[2000];
int server_struct_length = sizeof(server_addr);
// Clean buffers:
memset(server_message, '\0', sizeof(server_message));
memset(client_message, '\0', sizeof(client_message));
// Create socket:
socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(socket_desc < 0){
printf("Error while creating socket\n");
return -1;
}
printf("Socket created successfully\n");
// Set port and IP:
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(2000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// Get input from the user:
printf("Enter message: ");
gets(client_message);
// Send the message to server:
if(sendto(socket_desc, client_message, strlen(client_message), 0,
(struct sockaddr*)&server_addr, server_struct_length) < 0){
printf("Unable to send message\n");
return -1;
}
// Receive the server's response:
if(recvfrom(socket_desc, server_message, sizeof(server_message), 0,
(struct sockaddr*)&server_addr, &server_struct_length) < 0){
printf("Error while receiving server's msg\n");
return -1;
}
printf("Server's response: %s\n", server_message);
// Close the socket:
close(socket_desc);
return 0;
}
解释:
包括头文件,创建套接字,并且在一个sockaddr_in类型的变量中初始化服务器地址信息(与在服务器端完成的方式类似):
#include <sys/socket.h>
#include <arpa/inet.h>
int socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(2000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
不同于TCP,当客户端使用sendto()和recvfrom()发送和接收数据,每次都要给出服务器的信息:
sendto(socket_desc, client_message, strlen(client_message), 0, (struct sockaddr*)&server_addr, server_struct_length);
recvfrom(socket_desc, server_message, sizeof(server_message), 0,(struct sockaddr*)&server_addr, &server_struct_length);
如果客户端和服务器都在recvfrom()上等待对方的消息,就会发生死锁。
工作流程: