Linux TCP/UDP CS模型
目录TCP Server/TCP Client
在C语言中实现一个TCP服务器时,使用select
函数可以帮助我们同时监控多个文件描述符(包括socket)的状态,从而实现非阻塞的I/O操作。以下是一个简单的TCP服务器示例,它使用select
来同时监听多个客户端连接,并处理它们发送的数据。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/types.h>
#define SERVER_PORT 8080
#define MAX_CLIENTS 10
#define MAX_TIMEOUT 10
#define BUFFER_SIZE 1024
#define print(fmt, args...) printf("\033[1m[ %s ] %03d: "fmt"\033[0m\n\r", __FUNCTION__, __LINE__, ##args)
int main() {
int server_fd, new_socket, max_sd;
struct sockaddr_in address;
int addrlen, opt = 1;
fd_set readfds;
struct timeval tv;
char buffer[BUFFER_SIZE] = {0};
int i, valread;
// 创建socket文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(SERVER_PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
if (listen(server_fd, MAX_CLIENTS) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
max_sd = server_fd;
while (1) {
FD_ZERO(&readfds);
FD_SET(server_fd, &readfds);
tv.tv_sec = MAX_TIMEOUT;
tv.tv_usec = 0;
int activity = select(max_sd + 1, &readfds, NULL, NULL, &tv);
if ((activity < 0) && (errno != EINTR)) {
perror("select");
continue;
}
if (!FD_ISSET(server_fd, &readfds)) {
continue;
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
continue;
}
if ((valread = read(new_socket, buffer, BUFFER_SIZE)) > 0) {
buffer[valread] = '\0';
printf("Received from client %d: %s\n", new_socket, buffer);
send(new_socket, "Hello from server", strlen("Hello from server"), 0);
}
close(new_socket);
}
close(server_fd);
return 0;
}
在C语言中,创建一个TCP客户端主要涉及以下几个步骤:
- 创建套接字(socket)。
- 设置服务器的地址信息。
- 连接到服务器(connect)。
- 发送和接收数据。
- 关闭套接字。
以下是一个简单的TCP客户端的C语言实现:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080
#define BUFFER_SIZE 1024
int main() {
int sock;
struct sockaddr_in serv_addr;
char buffer[BUFFER_SIZE] = {0};
char *message = "Hello from client";
ssize_t bytes_sent, bytes_received;
// 创建socket文件描述符
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// 设置服务器的地址信息
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVER_PORT);
// 将IPv4地址从点分十进制转换为二进制形式
if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {
perror("invalid address/address not supported");
exit(EXIT_FAILURE);
}
// 连接到服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("connection failed");
exit(EXIT_FAILURE);
}
printf("Connected to server.\n");
// 发送消息到服务器
bytes_sent = send(sock, message, strlen(message), 0);
if (bytes_sent < 0) {
perror("send failed");
exit(EXIT_FAILURE);
} else {
printf("Sent %zd bytes: %s\n", bytes_sent, message);
}
// 从服务器接收响应
bytes_received = recv(sock, buffer, BUFFER_SIZE - 1, 0);
if (bytes_received < 0) {
perror("recv failed");
exit(EXIT_FAILURE);
} else if (bytes_received == 0) {
printf("Server closed the connection.\n");
} else {
buffer[bytes_received] = '\0';
printf("Received %zd bytes: %s\n", bytes_received, buffer);
}
// 关闭套接字
close(sock);
return 0;
}
这个程序会尝试连接到位于SERVER_IP
和SERVER_PORT
的服务器,并发送一个简单的消息Hello from client
。之后,它会尝试从服务器接收数据,并将其打印到控制台上。如果在send
或recv
函数中发生错误,程序会打印出错误信息并退出。
请注意,这个客户端程序假设服务器在发送完数据后会关闭连接,或者发送的数据量不会超过BUFFER_SIZE - 1
。在实际应用中,你可能需要处理更复杂的情况,比如服务器持续发送数据,或者数据可能分多个包到达。
编译和运行这个客户端程序,你需要一个TCP服务器正在监听SERVER_IP
和SERVER_PORT
。如果服务器没有运行,connect
调用将失败。
编译命令可能类似于:
gcc client.c -o client
然后运行编译好的程序:
./client
确保你的TCP服务器已经启动并监听在指定的IP地址和端口上。
UDP Server/UDP Client
在C语言中实现一个UDP服务器,你需要执行以下步骤:
- 创建一个UDP套接字(socket)。
- 绑定套接字到一个本地地址和端口。
- 接收客户端发送的数据(recvfrom)。
- 发送响应数据给客户端(sendto)。
- 关闭套接字。
以下是一个简单的UDP服务器示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define SERVER_PORT 8080
#define BUFFER_SIZE 1024
int main() {
int sock;
struct sockaddr_in serv_addr, cli_addr;
socklen_t cli_len;
char buffer[BUFFER_SIZE] = {0};
ssize_t bytes_received;
// 创建UDP套接字
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// 设置服务器地址信息
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(SERVER_PORT);
// 绑定套接字到地址和端口
if (bind(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
printf("Server started on port %d\n", SERVER_PORT);
cli_len = sizeof(cli_addr);
// 循环接收客户端数据
while (1) {
// 接收数据
bytes_received = recvfrom(sock, buffer, BUFFER_SIZE - 1, 0,
(struct sockaddr*)&cli_addr, &cli_len);
if (bytes_received < 0) {
perror("recvfrom failed");
break;
}
buffer[bytes_received] = '\0'; // 确保字符串正确结束
printf("Received from %s:%d: %s\n",
inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buffer);
// 发送响应
sendto(sock, "Message received", strlen("Message received"), 0,
(struct sockaddr*)&cli_addr, cli_len);
}
// 关闭套接字
close(sock);
return 0;
}
这段代码创建了一个UDP服务器,它监听在SERVER_PORT
定义的端口上。当接收到客户端发送的数据时,服务器会打印出数据并发送一个确认消息回客户端。
要编译和运行这个服务器,你可以使用类似下面的命令:
gcc udp_server.c -o udp_server
./udp_server
请确保没有其他的程序在SERVER_PORT
定义的端口上监听,否则bind
调用会失败。
为了测试这个服务器,你可以编写一个UDP客户端或者使用现有的网络工具(如nc
或netcat
)来发送UDP数据包到服务器的地址和端口。
例如,使用netcat
作为客户端发送消息给服务器:
echo "Hello from client" | nc -u -w1 localhost 8080
这里的-u
标志告诉netcat
使用UDP协议,-w1
设置等待响应的超时时间为1秒,localhost
是服务器的地址,8080
是服务器的端口。服务器应该接收并打印出消息,然后发送回一个响应。由于UDP是无连接的,客户端可能无法接收到服务器的响应,这取决于网络条件和客户端的实现。
在C语言中实现一个UDP客户端相对简单,主要步骤包括创建UDP套接字、向服务器发送数据以及接收服务器的响应。以下是一个简单的UDP客户端示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080
#define BUFFER_SIZE 1024
int main() {
int sock;
struct sockaddr_in serv_addr;
char *message = "Hello from UDP client";
char buffer[BUFFER_SIZE] = {0};
ssize_t bytes_sent, bytes_received;
// 创建UDP套接字
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// 设置服务器地址信息
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVER_PORT);
if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {
perror("invalid address/address not supported");
exit(EXIT_FAILURE);
}
// 发送数据到服务器
bytes_sent = sendto(sock, message, strlen(message), 0,
(struct sockaddr*)&serv_addr, sizeof(serv_addr));
if (bytes_sent < 0) {
perror("sendto failed");
exit(EXIT_FAILURE);
} else {
printf("Sent %zd bytes: %s\n", bytes_sent, message);
}
// 接收服务器的响应
bytes_received = recvfrom(sock, buffer, BUFFER_SIZE - 1, 0,
NULL, 0);
if (bytes_received < 0) {
perror("recvfrom failed");
} else if (bytes_received == 0) {
printf("Server closed the connection.\n");
} else {
buffer[bytes_received] = '\0'; // 确保字符串正确结束
printf("Received %zd bytes: %s\n", bytes_received, buffer);
}
// 关闭套接字
close(sock);
return 0;
}
这段代码实现了一个简单的UDP客户端,它向位于SERVER_IP
和SERVER_PORT
的服务器发送一条消息,并等待服务器的响应。
要编译和运行这个客户端,你可以使用以下命令:
gcc udp_client.c -o udp_client
./udp_client
请确保你的UDP服务器已经启动并监听在指定的IP地址和端口上。客户端会发送消息到服务器,并尝试接收服务器的响应。如果服务器没有发送响应,客户端的recvfrom
调用可能会阻塞,直到收到数据或发生错误。如果你希望客户端是非阻塞的,你需要在创建套接字后设置相应的套接字选项。
注意:UDP是无连接的协议,因此客户端发送消息后不一定能接收到服务器的响应,这取决于网络条件、服务器实现以及服务器的配置。如果服务器没有响应,客户端可能会无限期地等待。在实际应用中,你可能需要设置超时或重试机制来处理这种情况。
标签:serv,UDP,addr,TCP,SERVER,CS,服务器,include From: https://www.cnblogs.com/adam-ma/p/18083701