- 创建Socket:使用Socket函数创建Socket套接字,可以指定TCP或UDP协议。
- 监听连接:在服务器端,使用bind函数绑定IP地址和端口,然后使用listen函数开始监听连接请求。
- 接受连接:在服务器端使用accept函数接受客户端的连接请求,并返回一个新的套接字进行通信。
- 建立连接:在客户端,使用connect函数连接服务器,并返回一个新的套接字进行通信。
- 数据传输:在建立好连接后,进行数据的发送和接收。
- 关闭连接:在通信结束后,使用close函数关闭套接字。
server.c
/*************************************************************************
> File Name: server.c
> Author: licz
> Created Time: Sun 10 Sep 2023 01:47:01 PM CST
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define BUFFER_SIZE 1024
void displayWithTimestamp(const char* format, ...);
void printClientInfo(int clientSocket);
int main() {
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[BUFFER_SIZE],client_ip[1024];
struct sockaddr_in serv_addr, cli_addr;
int n;
displayWithTimestamp("Waiting for the socket connection to be established ...");
// 创建套接字,其中 AF_INET 表示使用 IPv4 地址族,SOCK_STREAM 表示使用 TCP 协议进行通信。
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Error opening socket");
exit(1);
}
displayWithTimestamp("Socket connection established successfully !");
// 初始化服务器地址结构
memset((char *)&serv_addr, 0, sizeof(serv_addr));
portno = 8848; // 设置监听的端口号
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(portno);//将端口号转换为网络字节序
// 绑定套接字到指定端口
if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Error on binding");
exit(1);
}
// 开始监听连接请求
listen(sockfd, 5);//将套接字设置为监听状态,并指定最大允许的连接数。
clilen = sizeof(cli_addr);
// 接受客户端连接
displayWithTimestamp("Wait for a client connection to be established...");
newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
if (newsockfd < 0) {
perror("Error on accept");
exit(1);
}
displayWithTimestamp("Client established successfully !");
printClientInfo(newsockfd);
// 打开文件用于保存数据
FILE *file = fopen("data.txt", "w");
if (file == NULL) {
perror("Error opening file");
exit(1);
}
// 接收数据并保存到文件
while ((n = read(newsockfd, buffer, BUFFER_SIZE)) > 0) {
fwrite(buffer, sizeof(char), n, file); // 写入文件
displayWithTimestamp("%s",buffer); // 打印到终端
memset(buffer, 0, BUFFER_SIZE); // 清空缓冲区
}
// 关闭文件和套接字
fclose(file);
close(newsockfd);
close(sockfd);
return 0;
}
void printClientInfo(int clientSocket) {
struct sockaddr_in clientAddress;
socklen_t clientAddressLength = sizeof(clientAddress);
// 获取客户端地址信息
if (getpeername(clientSocket, (struct sockaddr*)&clientAddress, &clientAddressLength) < 0) {
perror("获取客户端地址信息失败");
exit(EXIT_FAILURE);
}
char clientIP[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &(clientAddress.sin_addr), clientIP, INET_ADDRSTRLEN) == NULL) {
perror("获取客户端IP地址失败");
exit(EXIT_FAILURE);
}
displayWithTimestamp("连接来自 %s:%d\n", clientIP, ntohs(clientAddress.sin_port));
}
void displayWithTimestamp(const char* format, ...) {
// 获取当前时间戳
time_t currentTime;
time(¤tTime);
// 转换为本地时间结构
struct tm* localTime = localtime(¤tTime);
// 格式化时间戳
char timestampString[20];
strftime(timestampString, sizeof(timestampString), "%Y-%m-%d %H:%M:%S", localTime);
// 打印带有时间戳的信息
printf("[%s] ", timestampString);
// 使用可变参数列表打印消息内容
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
printf("\n");
}