一般的socket通信的过程是:服务器在接受来自客户的一个新连接时,会创建出一个新的套接字,而已有的监听套接字将继续接受来自客户端的请求并将它们放入队列中等待处理(调用accept处理)。
由accept获得的套接字描述符在主进程调用fork时,可以被子进程共享,因此,如果accept之后,在子进程中处理连接,在父进程中关闭套接字描述符,就可以处理一个客户端连接了。重复上述的过程,就用fork实现了server端的多客户编程。
需要注意的是主进程在创建了子进程后,并没有调用wait方法来等待它们的完成,这样容易出现僵尸进程,需要在主进程中设置忽略SIGCHLD信号以避免出现僵尸进程。
下面采用Linux程序设计上的例子来说明这个过程:
服务端
/* This program, server4.c, begins in similar vein to our last server,
with the notable addition of an include for the signal.h header file.
The variables and the procedure of creating and naming a socket are the same. */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <signal.h>
#include <unistd.h>
int main()
{
int server_sockfd, client_sockfd;
int server_len, client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(9734);
server_len = sizeof(server_address);
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
/* Create a connection queue, ignore child exit details and wait for clients. */
listen(server_sockfd, 5);
signal(SIGCHLD, SIG_IGN);
while(1) {
char ch;
printf("server waiting\n");
/* Accept connection. */
client_len = sizeof(client_address);
client_sockfd = accept(server_sockfd,
(struct sockaddr *)&client_address, &client_len);
/* Fork to create a process for this client and perform a test to see
whether we're the parent or the child. */
if(fork() == 0) {
/* If we're the child, we can now read/write to the client on client_sockfd.
The five second delay is just for this demonstration. */
read(client_sockfd, &ch, 1);
sleep(5);
ch++;
write(client_sockfd, &ch, 1);
close(client_sockfd);
exit(0);
}
/* Otherwise, we must be the parent and our work for this client is finished. */
else {
close(client_sockfd);
}
}
}
客户端:
/* Make the necessary includes and set up the variables. */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int main()
{
int sockfd;
int len;
struct sockaddr_in address;
int result;
char ch = 'A';
/* Create a socket for the client. */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
/* Name the socket, as agreed with the server. */
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = htons(9734);
len = sizeof(address);
/* Now connect our socket to the server's socket. */
result = connect(sockfd, (struct sockaddr *)&address, len);
if(result == -1) {
perror("oops: client3");
exit(1);
}
/* We can now read/write via sockfd. */
write(sockfd, &ch, 1);
read(sockfd, &ch, 1);
printf("char from server = %c\n", ch);
close(sockfd);
exit(0);
}
服务器程序将会创建新的子进程来处理客户连接。样例实现了客户端向服务端写入一个字符后,服务端读取该字符并协会该字符的下一个字符。例如客户端写入A,服务端写回B,客户端显示服务端写会的B。