Socket是网络通信的基础,C语言通过socket API实现TCP和UDP编程。本文详细介绍服务端和客户端的开发流程。
Socket基础
Socket是应用程序与网络协议栈之间的接口。常用的socket类型有:
- TCP (SOCK_STREAM):面向连接、可靠传输
- UDP (SOCK_DGRAM):无连接、不可靠但高效
TCP服务端
TCP服务端的开发步骤:创建socket、绑定地址、监听、接受连接、数据交换:
#include
#include
#include
#include
#include
#define PORT 8080
int main(void) {
int server_fd, client_fd;
struct sockaddr_in addr, client_addr;
char buffer[1024];
socklen_t client_len = sizeof(client_addr);
/* 创建socket */
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("socket");
return 1;
}
/* 设置地址复用 */
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
/* 绑定地址 */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("bind");
close(server_fd);
return 1;
}
/* 监听连接 */
listen(server_fd, 5);
printf("Server listening on port %d\n", PORT);
/* 接受连接 */
client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
printf("Client connected\n");
/* 接收数据 */
ssize_t n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
if (n > 0) {
buffer[n] = '\0';
printf("Received: %s\n", buffer);
/* 发送响应 */
const char *response = "Hello from server";
send(client_fd, response, strlen(response), 0);
}
close(client_fd);
close(server_fd);
return 0;
}
TCP客户端
TCP客户端的步骤:创建socket、连接服务器、发送和接收数据:
#include
#include
#include
#include
#include
#define SERVER_IP "127.0.0.1"
#define PORT 8080
int main(void) {
int sock;
struct sockaddr_in server_addr;
char buffer[1024];
/* 创建socket */
sock = socket(AF_INET, SOCK_STREAM, 0);
/* 设置服务器地址 */
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
/* 连接服务器 */
if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("connect");
return 1;
}
/* 发送数据 */
const char *message = "Hello server";
send(sock, message, strlen(message), 0);
/* 接收响应 */
ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);
if (n > 0) {
buffer[n] = '\0';
printf("Server response: %s\n", buffer);
}
close(sock);
return 0;
}
UDP编程
UDP是无连接的,不需要建立连接过程:
/* UDP服务端 */
int udp_server(void) {
int sock;
struct sockaddr_in addr, client_addr;
char buffer[1024];
socklen_t len = sizeof(client_addr);
sock = socket(AF_INET, SOCK_DGRAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(9000);
bind(sock, (struct sockaddr*)&addr, sizeof(addr));
/* 接收数据(无需accept) */
ssize_t n = recvfrom(sock, buffer, sizeof(buffer) - 1, 0,
(struct sockaddr*)&client_addr, &len);
buffer[n] = '\0';
printf("Received: %s\n", buffer);
close(sock);
}
非阻塞Socket
使用fcntl将socket设置为非阻塞模式,实现异步IO:
/* 设置非阻塞 */
int set_nonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
/* 使用select或epoll处理多连接 */
/* 详见后续C语言IO多路复用指南 */
网络编程建议
实际开发中推荐使用epoll或kqueue处理高并发,使用缓冲区减少系统调用,注意字节序转换。
总结
- TCP是面向连接的可靠传输协议
- UDP是无连接的高效传输协议
- 服务端需要bind、listen、accept流程
- 客户端使用connect建立连接
- 非阻塞IO适合高并发场景