一、问题引入
Linux网络编程:socket实现client/server通信 随笔简单介绍了TCP Server服务单客户端的socket通信,但是并未涉及多客户端通信。
对于网络编程肯定涉及到多客户端通信和并发编程 (指在同时有大量的客户链接到同一服务器),故本随笔补充这部分知识。
而且并发并发编程涉及到多进程、多线程,其中 fork()函数是Unix中派生新进程的唯一方法。
二、解决过程
2-1 server 代码
#include <stdlib.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h> /* inet(3) functions */
#include <unistd.h> /* fork(),read(),write() functions */
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#define IP "10.8.198.227"
#define PORT 8887
static int handle(int connect_fd, const char *socket);
static void sig_chld(int);
int main(void)
{
int listenfd, connfd;
struct sockaddr_in server_sockaddr;
struct sockaddr_in client_addr;
char buf[1024];
char client_socket[128];
socklen_t length;
int pid;
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(PORT);
server_sockaddr.sin_addr.s_addr = inet_addr(IP);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0)
{
perror("socket error");
exit(1);
}
if (bind(listenfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr)) < 0)
{
perror("bind error");
exit(1);
}
if (listen(listenfd, 5) < 0)
{
perror("listen error");
exit(1);
}
if (signal(SIGCHLD, sig_chld) == SIG_ERR)
{
perror("signal error");
exit(1);
}
for (;;)
{
// 接受来自客户端的信息
printf("accept start \n");
memset(&client_addr, 0, sizeof(client_addr));
length = sizeof(client_addr);
if ((connfd = accept(listenfd, (struct sockaddr *)&client_addr, &length)) < 0)
{
if (errno == EINTR)
continue;
else
{
perror("accept error");
exit(1);
}
}
printf("client addr:%s por:%d\n",
inet_ntop(AF_INET, &client_addr.sin_addr, buf, sizeof(buf)),
ntohs(client_addr.sin_port));
snprintf(client_socket, sizeof(client_socket), "client socket (%s:%d)",
inet_ntop(AF_INET, &client_addr.sin_addr, buf, sizeof(buf)),
ntohs(client_addr.sin_port));
pid = fork();
if (pid == 0) // 子进程
{
close(listenfd);
handle(connfd, client_socket);
exit(1);
}
else if (pid < 0) // error
{
close(connfd);
}
else // 父进程
{
close(connfd);
}
}
return EXIT_SUCCESS;
}
static int handle(int connect_fd, const char *socket)
{
int read_len;
char buf[1024];
for (;;)
{
memset(buf, 0, sizeof(buf));
read_len = read(connect_fd, buf, sizeof(buf));
if (read_len < 0)
{
printf("read error \n");
close(connect_fd);
return -1;
}
else if (read_len == 0)
{
printf("%s close \n", socket);
close(connect_fd);
break;
}
printf("%s:%s\n", socket, buf);
if (strcmp("exit", buf) == 0)
{
printf("%s close \n", socket);
close(connect_fd);
return 0;
}
}
return 0;
}
static void sig_chld(int signo)
{
pid_t pid;
int stat;
pid = wait(&stat);
printf("child %d terminated\n", pid);
return;
}
2-2 client 代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> /* inet(3) functions */
#include <unistd.h>
#define IP "10.8.198.227"
#define PORT 8887
int handle(int fd);
int main(void)
{
int sockfd;
char buf[1024];
struct sockaddr_in server_addr;
char server_socket[128];
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(IP);
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0)
{
printf("connect error \n");
return -1;
}
snprintf(server_socket, sizeof(server_socket), "server socket (%s:%d)",
inet_ntop(AF_INET, &server_addr.sin_addr, buf, sizeof(buf)),
ntohs(server_addr.sin_port));
printf("%s\n", server_socket);
handle(sockfd);
close(sockfd);
return EXIT_SUCCESS;
}
int handle(int connect_fd)
{
char send_buf[1024], recv_buf[1024];
for (;;)
{
memset(send_buf, 0, sizeof(send_buf));
memset(recv_buf, 0, sizeof(recv_buf));
while (fgets(send_buf, sizeof(send_buf), stdin) != NULL)
{
if (send_buf[strlen(send_buf) - 1] == '\n')
send_buf[strlen(send_buf) - 1] = '\0';
write(connect_fd, send_buf, strlen(send_buf));
if (strcmp("exit", send_buf) == 0)
{
return 0;
}
}
}
return 0;
}
2-3 运行测试
1、client 1 连接 server
2、client 2 连接 server
3、多客户端与服务器通信