C语言实现并发服务器主要有多线程和多进程两种方式,以下是详细介绍:
1、原理:主线程负责接受客户端连接,一旦有新的客户端连接到服务器,就立即创建一个子线程,在子线程的线程处理函数中专门处理该客户端的读写请求。
2、代码示例
服务器端
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #include <arpa/inet.h> #include <unistd.h> #include <pthread.h> #define ERRLOG(errmsg) do { printf("%s--%s(%d):", __FILE__, __func__, __LINE__); perror(errmsg); exit(-1); } while (0) typedef struct { int accept_fd; struct sockaddr_in client_addr; } num_t; int socket_bind_listen(const char *argv[]); void *task1(void *arg); int main(int argc, const char *argv[]) { if (3 != argc) { printf("Usage : %s <IP> <PORT> ", argv[0]); exit(-1); } int sockfd = socket_bind_listen(argv); struct sockaddr_in client_addr; memset(&client_addr, 0, sizeof(client_addr)); socklen_t client_addr_len = sizeof(client_addr); num_t ku; memset(&ku, 0, sizeof(ku)); pthread_t tid; while (1) { ku.accept_fd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addr_len); if (-1 == ku.accept_fd) ERRLOG("accept error"); printf("客户端 (%s:%d) 连接了 ", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); ku.client_addr = client_addr; if (pthread_create(&tid, NULL, task1, (void *)&ku)) ERRLOG("create tid2 error"); pthread_detach(tid); } close(sockfd); return 0; } int socket_bind_listen(const char *argv[]) { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == sockfd) ERRLOG("socket error"); struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(atoi(argv[2])); server_addr.sin_addr.s_addr = inet_addr(argv[1]); if (-1 == bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr))) ERRLOG("bind error"); if (-1 == listen(sockfd, 128)) ERRLOG("listen error"); return sockfd; } void *task1(void *arg) { num_t *ku = (num_t *)arg; char buf[1024]; int len; while (1) { len = read(ku->accept_fd, buf, sizeof(buf)); if (len <= 0) { printf("客户端断开连接 "); break; } printf("接收到来自客户端的消息: %s ", buf); write(ku->accept_fd, buf, len); } close(ku->accept_fd); return NULL; }
客户端
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #define DEST_PORT 9999 //目标地址端口号 #define DEST_IP "127.0.0.1" //目标地址IP,这里设为本机,不一定非得是127.0.0.1,只要127开头并且不是127.0.0.0和127.255.255.255即可 #define MAX_DATA 100 //接收到的数据最大程度 int main() { int sockfd; struct sockaddr_in dest_addr; char buf[MAX_DATA]; sockfd = socket(AF_INET, SOCK_STREAM, 0); dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(DEST_PORT); dest_addr.sin_addr.s_addr = inet_addr(DEST_IP); bzero(&(dest_addr.sin_zero), 8); connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)); printf("connect success "); while (1) { char send_buf[512] = ""; scanf("%s", &send_buf); write(sockfd, send_buf, sizeof(send_buf)); read(sockfd, buf, sizeof(buf)); printf("client receive:%s ", buf); } return 0; }
1、原理:每个客户端连接由一个独立的进程处理,进程间相互隔离,互不影响,提高了稳定性,利用操作系统的进程管理和调度机制,实现并发处理。
2、代码示例
服务器端
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> void handle_client(int client_socket) { char buffer[256]; int n; bzero(buffer, 256); n = read(client_socket, buffer, 255); if (n < 0) perror("ERROR reading from socket"); printf("Here is the message: %s ", buffer); n = write(client_socket, "I got your message", 18); if (n < 0) perror("ERROR writing to socket"); close(client_socket); } int main(int argc, char *argv[]) { int sockfd, newsockfd, portno; socklen_t clilen; struct sockaddr_in serv_addr, cli_addr; if (argc < 2) { fprintf(stderr,"ERROR, no port provided "); exit(1); } sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("ERROR opening socket"); exit(1); } bzero((char *) &serv_addr, sizeof(serv_addr)); portno = atoi(argv[1]); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = 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); while (1) { newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); if (newsockfd < 0) { perror("ERROR on accept"); continue; } int pid = fork(); if (pid < 0) { perror("ERROR on fork"); close(newsockfd); continue; } if (pid == 0) { // child process close(sockfd); handle_client(newsockfd); exit(0); } else { // parent process close(newsockfd); // parent doesn't need this } } close(sockfd); return 0; // this will never be executed }