基础版
一次信息传递后双端退出
服务器端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define PORT 8080
#define BUF_SIZE 1024
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUF_SIZE] = {0};
char *hello = "Hello from server";
// 创建socket文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 绑定socket到端口8080
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听端口
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
// 等待客户端连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
perror("accept");
exit(EXIT_FAILURE);
}
// 读取客户端发送的数据
read(new_socket, buffer, BUF_SIZE);
printf("%s\n", buffer);
// 发送数据到客户端
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sent\n");
// 关闭socket
close(server_fd);
close(new_socket);
return 0;
}
1. socket()
函数
功能:创建一个新的套接字。
原型:int socket(int domain, int type, int protocol);
参数:
domain
:指定协议族。对于IPv4,通常是AF_INET
;对于IPv6,是AF_INET6
。type
:指定套接字类型。对于TCP连接,使用SOCK_STREAM
;对于UDP连接,使用SOCK_DGRAM
。protocol
:指定具体的协议。通常设置为0,让系统自动选择适合domain
和type
的协议。
返回值:成功时返回一个新的套接字文件描述符,失败时返回-1并设置errno。
2. bind()
函数
功能:将套接字绑定到一个特定的IP地址和端口号上。
原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd
:要绑定的套接字文件描述符。addr
:指向sockaddr
结构的指针,该结构包含IP地址和端口号。对于IPv4,通常使用sockaddr_in
结构。addrlen
:addr
参数指向的结构的长度。
返回值:成功时返回0,失败时返回-1并设置errno。
3. listen()
函数
功能:使套接字进入监听状态,准备接受客户端的连接请求。
原型:int listen(int sockfd, int backlog);
参数:
sockfd
:要监听的套接字文件描述符。backlog
:指定系统应该为等待处理的连接请求所排队的最大数量。
返回值:成功时返回0,失败时返回-1并设置errno。
4. accept()
函数
功能:接受一个连接请求,如果连接被接受,则创建一个新的套接字用于与客户端通信。
原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd
:处于监听状态的套接字文件描述符。addr
:一个输出参数,用于存储客户端的地址信息。如果不需要,可以设置为NULL。addrlen
:一个输入/输出参数,指定addr
的大小,并在成功时更新为实际返回的地址长度。
返回值:成功时返回一个新的套接字文件描述符,用于与客户端通信;失败时返回-1并设置errno。
5. send()
和 recv()
函数(或在UNIX/Linux中使用send()
/sendto()
和recv()
/recvfrom()
)
功能:
send()
:通过套接字发送数据。recv()
:从套接字接收数据。
注意:在UNIX/Linux中,send()
和recv()
是更通用的函数,用于TCP连接。对于UDP,通常使用sendto()
和recvfrom()
,因为它们允许指定数据的目的地址和来源地址。
原型(以send()
为例):ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
sockfd
:套接字文件描述符。buf
:指向要发送数据的缓冲区的指针。len
:要发送数据的长度。flags
:通常设置为0。
返回值:成功时返回发送的字节数,失败时返回-1并设置errno。
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 8080
int main() {
struct sockaddr_in serv_addr;
int sock = 0;
char *hello = "Hello from client";
char buffer[1024] = {0};
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// 将IPv4地址从文本转换成二进制形式
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {
printf("\nInvalid address/ Address not supported \n");
return -1;
}
// 连接到服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
printf("\nConnection Failed \n");
return -1;
}
// 发送数据到服务器
send(sock, hello, strlen(hello), 0);
printf("Hello message sent\n");
// 接收服务器回显的数据
int valread = read(sock, buffer, 1024);
printf("%s\n", buffer);
// 关闭socket
close(sock);
return 0;
}
进阶版
重复进行数据交换,直到客户端输入exit
服务器端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define PORT 8080
#define BUF_SIZE 1024
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUF_SIZE] = {0};
char *hello = "Hello from server";
// 创建socket文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 绑定socket到端口8080
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听端口
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
// 等待客户端连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
perror("accept");
exit(EXIT_FAILURE);
}
while (1) {
// 读取客户端发送的数据
int bytes_read = read(new_socket, buffer, BUF_SIZE - 1); // 保留一个字节给 '\0'
if (bytes_read < 0) {
perror("read failed");
break; // 或者可以关闭 socket 并退出循环
}
buffer[bytes_read] = '\0'; // 确保字符串以 '\0' 结尾
// 检查是否接收到 "exit"
if (strcmp(buffer, "exit") == 0) {
break; // 退出循环
}
printf("%s\n", buffer);
// 发送数据到客户端
send(new_socket, hello, strlen(hello), 0);
printf("Message sent: %s\n", hello);
}
// 关闭socket
close(server_fd);
close(new_socket);
return 0;
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
struct sockaddr_in serv_addr;
int sock = 0;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// 将IPv4地址从文本转换成二进制形式
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {
printf("\nInvalid address/ Address not supported \n");
return -1;
}
// 连接到服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
printf("\nConnection Failed \n");
return -1;
}
char buffer[BUFFER_SIZE] = {0}; // 用于接收服务器响应的缓冲区
char input_buffer[BUFFER_SIZE] = {0}; // 用于存储用户输入的缓冲区
while (1) {
printf("Type in the message you want to send (or 'exit' to quit): ");
if (fgets(input_buffer, sizeof(input_buffer), stdin) == NULL) {
perror("fgets failed");
break;
}
// 去除 fgets 读取的换行符(如果有)
input_buffer[strcspn(input_buffer, "\n")] = 0;
if (strcmp(input_buffer, "exit") == 0) {
break;
}
// 发送数据到服务器
send(sock, input_buffer, strlen(input_buffer), 0);
printf("Message sent\n");
// 接收服务器回显的数据
int valread = read(sock, buffer, BUFFER_SIZE - 1);
if (valread < 0) {
perror("read failed");
break;
}
buffer[valread] = '\0'; // 确保字符串以空字符结尾
printf("%s\n", buffer);
}
// 关闭socket
close(sock);
return 0;
}