说在前面
功能介绍
- 客户从标准输入(例如键盘)读入一行文本,并写给服务器
- 服务器从网络输入读入这行文本,并回射(转发)给客户
- 客户从网络输入读入这行回射文本,并显示于标准输出(例如屏幕)上
客户端与服务器之间是全双工连接(即可同时发送和接收);writen、readline见⟅UNIX网络编程⟆⦔readn、writen、readline函数
服务器程序
#include "unp.h"
#define SERV_PORT 9877
void
str_echo(int sockfd)
{
ssize_t n;
char buf[MAXLINE];
for( ; ; )
{
while( (n = read(sockfd, buf, MAXLINE)) > 0)
Writen(sockfd, buf, n);
if(n < 0 && errno == EINTR)
continue;
else if(n < 0)
err_sys("str_echo: read error");
else
break;
}
}
int
main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
for ( ; ; ) {
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (struct sockaddr *) &cliaddr, &clilen);
if ( (childpid = Fork()) == 0) {
Close(listenfd);
str_echo(connfd);
exit(0);
}
Close(connfd);
}
}
- main函数中各种细节见目录中基础部分。
主进程(父进程)在accept接受一个连接后,调用fork函数产生一个子进程,子进程继续处理该连接来自客户的请求。
- 关于str_echo函数
str_echo函数只是将read到的数据发送(write)回客户。
在该函数的read调用时,默认单次read不保证读取完所有客户的数据,因此使用了while循环。
- Writen
Writen为函数writen的一个包裹函数。上述代码中,所有首字母大写的均为包裹函数。具体实现见代码。
客户端程序
#include "unp.h"
#define SERV_PORT 9877
void
str_cli(FILE *fp, int sockfd)
{
char sendline[MAXLINE], recvline[MAXLINE];
while(Fgets(sendline, MAXLINE, fp) != NULL)
{
Writen(sockfd, sendline, strlen(sendline));
if(Readline(sockfd, recvline, MAXLINE) == 0)
err_quit("str_cli: server terminated prematurely");
Fputs(recvline, stdout);
}
}
int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("usage: tcpcli <IPaddress>");
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
Connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
str_cli(stdin, sockfd);
exit(0);
}
- main函数中各种细节见目录中基础部分。
客户端是一些基本流程,主要工作交由str_cli函数处理。
- str_cli函数
在主函数中,str_cli的实参是stdin,表示标准输入,在头文件“stdio”中定义。
在str_cli函数中,由fgets函数从标准输入中读取一行;Fgets(sendline, MAXLINE, fp)
然后,将读取到的数据使用Writen函数发送给服务器;Writen(sockfd, sendline, strlen(sendline));
之后,使用Readline函数接收服务器的回射数据,并存储到recvline中;Readline(sockfd, recvline, MAXLINE)
最后,使用fputs函数将recvline中的数据发送给标准输出。Fputs(recvline, stdout);
- Writen
Writen为函数writen的一个包裹函数。
- Readline
Readline为函数readline的一个包裹函数。上述代码中,所有首字母大写的均为包裹函数。具体实现见代码。
代码
编译
- 使用cmake。
- 文件结构
- 编译
cmake .
make
运行