下面用多进程方式实现一个TCP并发服务器,每当一个新的客户端连接时fork一个子进程去和它通信。(各种主流的web服务器都不是纯粹的多进程方式运行,比如apache在每个进程中都用多路复用方式,直到连接数多到超过select可监听的最大描述符数时也会利用新的进程去处理。)
服务器端(server.c)
#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>
#define MAX_LISTEN 5
#define PORT 1987
#define IP "127.0.0.1"
void serve_proc(int conn_fd)
{
int recv_num;
int send_num;
char recv_buf[100];
char send_buf[100];
int pid = getpid();
printf("client %d started\n", pid);
while (1) {
//receive
recv_num = recv(conn_fd, recv_buf, sizeof(recv_buf), 0);
if (recv_num < 0) {
close(conn_fd);
exit(1);
}
recv_buf[recv_num] = '\0';
printf("child proc %d got : %s\n", pid, recv_buf);
if (strcmp(recv_buf,"quit") == 0) {
printf("child proc %d quit\n", pid);
break;
}
//send
sprintf(send_buf, "server proc got %d bytes\n", recv_num);
send_num = send(conn_fd, send_buf, strlen(send_buf), 0);
if (send_num < 0) {
close(conn_fd);
exit(1);
}
printf("child proc %d sent : %s\n", pid, send_buf);
}
close(conn_fd);
}
int main()
{
pid_t pid;
int conn_fd;
int sock_fd = socket(AF_INET,SOCK_STREAM,0);
if (sock_fd < 0) {
perror("create socket failed");
exit(1);
}
struct sockaddr_in addr_client;
int client_size = sizeof(struct sockaddr_in);
struct sockaddr_in addr_serv;
memset(&addr_serv, 0, sizeof(addr_serv));
addr_serv.sin_family = AF_INET;
addr_serv.sin_port = htons(PORT);
addr_serv.sin_addr.s_addr = inet_addr(IP);
if (bind(sock_fd,(struct sockaddr *)&addr_serv,sizeof(struct sockaddr_in)) < 0) {
perror("bind error");
exit(1);
}
if (listen(sock_fd,MAX_LISTEN) < 0) {
perror("listen failed");
exit(1);
}
while (1) {
conn_fd = accept(sock_fd, (struct sockaddr *)&addr_client, &client_size);
if (conn_fd < 0) {
perror("accept failed");
exit(1);
}
pid = fork();
if (pid == 0) { // child proc
close(sock_fd);
serve_proc(conn_fd);
} else { // main proc
close(conn_fd);
}
}
close(sock_fd);
return 0;
}
客户端(client.c)
#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>
#define REMOTE_IP "127.0.0.1"
#define REMOTE_PORT 1987
int main()
{
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0) {
perror("create socket failed");
exit(1);
}
struct sockaddr_in addr_serv;
memset(&addr_serv, 0 ,sizeof(addr_serv));
addr_serv.sin_family = AF_INET;
addr_serv.sin_port = htons(REMOTE_PORT);
addr_serv.sin_addr.s_addr = inet_addr(REMOTE_IP);
if (connect(sock_fd, (struct sockaddr *)&addr_serv, sizeof(struct sockaddr)) < 0) {
perror("connect failed");
exit(1);
}
int send_num, recv_num;
char send_buf[100],recv_buf[100];
while (1) {
printf("Input MSG: ");
scanf("%s",send_buf);
send_num = send(sock_fd, send_buf, strlen(send_buf), 0);
if (send_num < 0) {
perror("send error");
exit(1);
}
recv_num = recv(sock_fd, recv_buf, sizeof(recv_buf), 0);
if (recv_num < 0) {
perror("recv error");
exit(1);
}
recv_buf[recv_num] = '\0';
printf("Got : %s\n", recv_buf);
}
close(sock_fd);
return 0;
}
代码都很简单啦,就是基本的收发数据。不过需要注意的一点是
fork子进程以后需要对子进程关闭监听套接口,对父进程关闭连接套接口。