这个简单例子是执行如下步骤的一个回射服务器
(1)客户从标准输入读入一行文本,并写给服务器
(2)服务器从网络输入读入这行文本,并回射给客户
(3)客户从网络输入读入这行回射文本,并显示在标准输出上
我们在客户与服务器之间画了两个箭头,不过它们实际上构成一个全双工的TCP连接。fgets和fputs这两个函数来自标准I/O库,
尽管我们将开发自己的回射服务器实现大多数TCP/IP实现却已经提供这样的服务器,既有使用TCP也有使用UDP。我们还将与自己的客户使用这些服务器。
回射输入行这样一个客户端/服务器程序是一个尽管简单而有效的网络应用程序例子。实现任何客户/服务器网络应用所需的所有基本步骤可通过本例子阐明。若想把本例子扩充成你自己的应用程序,你只需要该服务器对来自客户的输入处理过程即可
除了以正常方式运行本客户和服务器(即键入一行文本并观察它的回射)之外,我们还会探讨它的许多边界条件:客户和服务器启动时发生了什么?
服务器端代码
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
//出错函数
#define err_exit(m)\
{\
perror(m);\
exit(EXIT_FAILURE);\
}
//最大连接数
#define LISTENQ 1024
//服务器端口号
#define SERV_PORT 9877
//接收和发送的缓冲区大小
#define BUFSIZE 4096
//处理客户端请求函数
void str_echo(int confd);
int main(int argc, char **argv)
{
int confd, listenfd;
struct sockaddr_in cliaddr, servaddr;
pid_t childpid;
socklen_t clilen;
int status;
char buff[BUFSIZE];
//设置协议地址结构内容
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
listenfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
if (listenfd == -1)
err_exit("socket");
status = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//将协议、IP地址、端口绑定到套接字
if (status == -1)
err_exit("bind");
status = listen(listenfd, LISTENQ);//使套接字变为监听套接字
if (status == -1)
err_exit("listen");
while (1)
{
clilen = sizeof(cliaddr);//这一步最容易忘记
confd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);//等待连接完成
if (status == -1)
err_exit("accept");
if ((childpid = fork()) == 0)//并发服务器,fork一个子进程来处理客户端请求
{
printf("connection from %s, port %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
ntohs(cliaddr.sin_port));
close(listenfd);//子进程不需要监听套接字
str_echo(confd);//子进程处理客户端请求
close(confd);//处理结束,关闭连接套接字