您的位置:首页 > 理论基础 > 计算机网络

系统编程-网络-服务器并发编程模型

2021-05-06 16:59 573 查看

 

之前介绍的服务器端代码只是基础功能,在支持多客户端访问时将面临困局。因为,我们来介绍服务器并发编程模型。

 

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程序

 

 

 

.

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: