之前介绍的服务器端代码只是基础功能,在支持多客户端访问时将面临困局。因为,我们来介绍服务器并发编程模型。
server.c
#include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdint.h> #include <string.h> #include "server.h" #include <assert.h> #include <sys/types.h> #include <unistd.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h> #include <errno.h> // 在Linux网络编程这块,,胡乱包含过多头文件会导致编译不过。 //#include <linux/tcp.h> // 包含下方这个头文件,就不能包含该头文件,否则编译报错。 #include <netinet/tcp.h> // setsockopt 函数 需要包含此头文件 int server_local_fd, new_client_fd; void sig_deal(int signum){ if(signum == SIGINT){ close(new_client_fd); close(server_local_fd); exit(0); }else if(signum == SIGCHLD){ wait(NULL); } } int main(void) { struct sockaddr_in sin; signal(SIGINT, sig_deal); signal(SIGCHLD, sig_deal); printf("pid = %d \n", getpid()); /*1.创建IPV4的TCP套接字 */ server_local_fd = socket(AF_INET, SOCK_STREAM, 0); if(server_local_fd < 0) { perror("socket error!"); exit(1); } /* 2.绑定在服务器的IP地址和端口号上*/ /* 2.1 填充struct sockaddr_in结构体*/ bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SERV_PORT); #if 0 // 方式一 sin.sin_addr.s_addr = inet_addr(SERV_IPADDR); #endif #if 0 // 方式二: sin.sin_addr.s_addr = INADDR_ANY; #endif #if 1 // 方式三: inet_pton函数来填充此sin.sin_addr.s_addr成员 if(inet_pton(AF_INET, "192.168.1.21", &sin.sin_addr.s_addr) >0 ){ char buf[16] = {0}; printf("s_addr=%s \n", inet_ntop(AF_INET, &sin.sin_addr.s_addr, buf, sizeof(buf))); printf("buf = %s \n", buf); } #endif /* 2.2 绑定*/ if(bind(server_local_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("bind"); exit(1); } /*3.listen */ listen(server_local_fd, 5); printf("client listen 5. \n"); char sned_buf[] = "hello, i am server \n"; struct sockaddr_in clientaddr; socklen_t clientaddrlen; char client_commu_recv_data_buf[100]={0}; char client_commu_send_data_buf[100]= {"I am server\n"}; while(1){ /*4. accept阻塞等待客户端连接请求 */ /****获取连接上来的客户端的信息******/ memset(&clientaddr, 0, sizeof(clientaddr)); memset(&clientaddrlen, 0, sizeof(clientaddrlen)); clientaddrlen = sizeof(clientaddr); /*** * 由于cliaddr_len是一个传入传出参数(value-result argument), * 传入的是调用者提供的缓冲区的长度以避免缓冲区溢出问题, * 传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区). * 所以,每次调用accept()之前应该重新赋初值。 * ******/ if( (new_client_fd = accept(server_local_fd, (struct sockaddr*)&clientaddr, &clientaddrlen)) < 0) { perror("accept"); exit(1); } printf("new client connected! print the client info .... \n"); int port = ntohs(clientaddr.sin_port); char ip[16] = {0}; inet_ntop(AF_INET, &(clientaddr.sin_addr.s_addr), ip, sizeof(ip)); printf("client: ip=%s, port=%d \n", ip, port); pid_t pid = fork(); if(pid < 0){ continue; }else if(0 == pid){ // child process close(server_local_fd); printf("server goes to read... \n"); int bytes_read_done = read(new_client_fd, client_commu_recv_data_buf, sizeof(client_commu_recv_data_buf)); printf("bytes_read_done = %d \n", bytes_read_done); // sleep(10); printf("strlen(client_commu_send_data_buf) = %d \n", strlen(client_commu_send_data_buf)); int bytes_write_done = write(new_client_fd, client_commu_send_data_buf, strlen(client_commu_send_data_buf)); printf("bytes_write_done = %d \n", bytes_write_done); if(bytes_write_done < 0){ if(errno == EPIPE){ printf("server : write -> EPIPE \n"); close(new_client_fd); exit(0); } } printf("--Server deal this client over! \n"); close(new_client_fd); exit(0); }else{ // parent process close(new_client_fd); } } // the following code will nerver run .... printf("server process end... \n"); close(server_local_fd); return 0; }
client.c
#include <unistd.h> #include<unistd.h> #include<stdlib.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<netdb.h> #include<string.h> #include<errno.h> #include<stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> #define PORT 5001 #define SERVER_IP "192.168.1.21" void sig_handler(int signo){ printf("sig_handler=> pid: %d, signo: %d \n", getpid(), signo); } int main() { int sockfd; struct sockaddr_in server_addr; struct hostent *host; if(signal(SIGPIPE, sig_handler) == SIG_ERR){ //if(signal(SIGPIPE, SIG_DFL) == SIG_ERR){ // SIGPIPE信号的默认执行动作是terminate(终止、退出),所以本进程会退出。 perror("signal error"); } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { fprintf(stderr, "Socket Error is %s\n", strerror(errno)); exit(EXIT_FAILURE); } bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); if (connect(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)) == -1) { fprintf(stderr, "Connect failed\n"); exit(EXIT_FAILURE); } char sendbuf[1024] = {0}; char recvbuf[2014] = {0}; while (1) { fgets(sendbuf, sizeof(sendbuf), stdin); printf("strlen(sendbuf) = %d \n", strlen(sendbuf)); if (strcmp(sendbuf, "exit\n") == 0){ printf("while(1) -> exit \n"); break; } send(sockfd, sendbuf, strlen(sendbuf), 0); recv(sockfd, recvbuf, sizeof(recvbuf), 0); fputs(recvbuf, stdout); memset(sendbuf, 0, sizeof(sendbuf)); memset(recvbuf, 0, sizeof(recvbuf)); } close(sockfd); printf(" client process end \n"); return 0; }
编译运行:
服务器程序
客户端1、客户端2程序
.
原文:https://www.cnblogs.com/happybirthdaytoyou/p/14674537.html