📄 vcserver.c
字号:
/* vcserver.c server */
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> /* sockaddr_in 结构体 */
/* 下面的这个include可以使程序查询主机名字列表,
找出所有与其IP绑定的hostname */
#include <netdb.h> /* 预定义/etc/hosts列表 */
main (int argc, char *argv[])
{
int rc, /* 系统调用return code */
new_sd, sock, /* server/listen 套接字标识符 */
adrlen, /* sockaddr 长度 */
cnt; /* number of bytes I/O */
struct sockaddr_in myname; /* Internet套接字名称 */
struct sockaddr_in *nptr; /* ptr 获取端口号 */
struct sockaddr addr; /* 通用套接字名称 */
char buf[80]; /* I/O 缓冲区定义 */
/* 在 /etc/hosts 文件中查找 */
struct hostent *hp, *gethostbyaddr();
/* 标识服务器进程,打印其PID */
printf("\nThis is the network server with pid %d\n",
getpid() );
/* 建立一个倾听套接字 */
if (( sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
printf("network server socket failure %d\n", errno);
perror("network server");
exit(1);
}
/* 初始化套接字地址结构中的各个参数 */
myname.sin_family = AF_INET; /* Internet地址 */
myname.sin_port = 0; /* 系统会绑定端口号 */
myname.sin_addr.s_addr = INADDR_ANY;
/* 将IP地址结构与套接字绑定,调用bind()函数 */
if (bind(sock, &myname, sizeof(myname) ) < 0 ) {
close(sock); /* defensive programming */
printf("network server bind failure %d\n", errno);
perror("network server");
exit(2);
}
/* 获取套接字端口号。调用getsockname()可以得到与套接字相关联
的端口号,并作为套接字地址结构中的一部分返回。记录
这个端口号以供客户端程序使用。
*/
adrlen = sizeof(addr); /* 地址长度为整形值 */
if ( ( rc = getsockname( sock, &addr, &adrlen ) ) < 0 )
{
printf("setwork server getsockname failure %d\n",
errno);
perror("network server");
close (sock);
exit(3);
}
/* 调试说明:
通用套接字地址addr被用来存放getsockname()返回的套接字的值。
打印这个信息。在一般的地址声明中,除了地址族其余的都被定
义为字符串类型。Getsocknae()调用之后,这个通用套接字地址
结构addr就会被用来存放关于客户端进程的信息。*/
printf("\nAfter getsockname():");
printf(" server listen socket data\n");
printf("\taddr.sa_family field value is: %d\n",
addr.sa_family);
printf("\taddr.sa_data string is %d bytes long;\n",
sizeof ( addr.sa_data ) );
printf("\taddr.sa_data string is:");
for ( cnt = 0; cnt < sizeof (addr.sa_data); cnt++)
printf(" %x", addr.sa_data[cnt]);
printf("\n");
/* 现在需要记录下来套接字的端口号。在这里端口号将作为启动客户端程
序的一个命令行参数使用。 */
/* 注意指针nptr,它是指向通用套接字地址结构的一个指针 */
nptr = (struct sockaddr_in *) &addr; /* port # */
printf("\n\tnetwork server: server has port number: %d\n",
ntohs ( nptr -> sin_port ) );
/* 调用listen()函数 */
if ( listen ( sock, 5 ) < 0 ) {
printf("network server bind failure %d\n", errno);
perror("network server");
close (sock);
exit(4);
}
/* 调试输出:
下面输出结构体myname中所包含的套接字信息 */
printf("Server has set up client socket with values:\n");
printf("\tInternet address is %lx\n", myname.sin_addr.s_addr);
printf("\tPort number used is %d\n", myname.sin_port);
printf("\tInternet family ID is %d\n", myname.sin_family);
printf("\tValues are filled in after connection request ");
printf("is accepted.");
/* 设置初始死循环以等待客户端进程的连接请求。由于结构体myname已经和倾听套接字绑
定,通过accept()调用可以获得套接字结构名称和套接字长度参数。 */
while (1) {
if ( ( new_sd = accept ( sock, 0, 0 ) ) < 0 ) {
printf("network server accept failure %d\n", errno);
perror("network server");
close (sock);
exit(5);
}
/* 通过fork()函数建立子进程来处理客户端服务请求。*/
if ( ( fork() ) == 0 ) { /* 子进程 */
int pid;
pid = getpid(); /* 获取子进程的PID */
close (sock); /* 子进程中不再需要父进程的参与 */
/* 查找出客户端所在。注意通用套接字地址结构addr的用法,获取客户端信息 */
if ((rc = getpeername( new_sd, &addr, &adrlen )) < 0) {
printf("network server %d getpeername failure %d\n",
pid, errno);
perror("network server");
close(new_sd);
exit(6);
}
/* 答应客户端信息。由于指针nptr是一个sockaddr_in结构体指针,
在这个结构体中的地址名称就可以传递给通用地址结构体addr。 */
printf("\n\tnetwork server %d:", pid);
printf(" client socket from host %s\n",
inet_ntoa ( nptr -> sin_addr ) );
printf("\t has port number %d\n",nptr -> sin_port);
/* 现在找到了所有与客户端相关的信息,并且可以通过在/etc/hosts
中查找来确定客户端的域名 */
if (( hp = gethostbyaddr (&nptr -> sin_addr,4,AF_INET))
!= NULL ) {
printf ("\tfrom hostname: %s\n\twith aliases: ",
hp -> h_name );
while ( *hp -> h_aliases )
printf ("\n\t\t\t%s", *hp -> h_aliases++ );
printf("\n\n");
}
else {
printf("network server %d ", pid);
printf("gethostbyaddr failure %d\n", h_errno);
perror("network server");
}
/* 与客户端交换数据。首先清除缓冲区 */
do {
memset (buf,0,sizeof(buf)); /* 清空缓冲区 */
/* bzero( buf, sizeof(buf));* 清空缓冲区, BSD 调用. */
/* 用read()函数从套接字缓冲区中读取远程客户端发送的数据,如果
数据长度为0则退出。*/
if (( cnt = read (new_sd, buf, sizeof(buf))) < 0 ) {
printf("network server %d ", pid);
printf("socket read failure &d\n", errno);
perror("network server");
close(new_sd);
exit(7);
}
else
if (cnt == 0) {
printf("network server received message");
printf(" of length %d\n", cnt);
printf("network server closing");
printf(" client connection...\n");
close (new_sd);
continue; /* 循环终止点 */
}
else {
/* 打印出接收到的信息,并发送回执 */
printf("network server %d received message",pid);
printf(" of length %d\n", cnt);
printf("network server %d received", pid);
printf(" the message %s\n", buf);
memset (buf,0,sizeof(buf)); /* 清空缓冲区 */
/* bzero( buf, sizeof(buf)); * 清空缓冲区, BSD 调用. */
strcpy(buf, "Message from server to client");
write (new_sd, buf, sizeof(buf));
} /* end of message-print else */
} /* end of do loop */
while (cnt != 0); /* 循环条件 */
exit(0); /* 子进程结束 */
} /* End of if-child-process */
else /* 非子进程,父进程条件判断 */
close (new_sd); /* 父进程不需要此套接字 */
} /* end of while (1) */
} /* end of main procedure */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -