源代码阅读与分析
TCP三次握手的流程图
为了进一步明晰地看到客户端、服务器通信方式,还是先要把这个三次握手流程放在这里,作为阅读源码的比较分析
TCP回射程序
服务器
#include "unp.h"
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; //Ipv4
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//监听任意一个端口。
servaddr.sin_port = htons(SERV_PORT);//绑定自己的固定端口号
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));//服务器三部曲②
Listen(listenfd, LISTENQ);//服务器三部曲③
for ( ; ; ) {
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);//连接。一般没有的话阻塞等待,类似一个循环。
//返回指向该套接字的文件描述符,如果套接字被标记为Non-blocking,
//队列中也没有等待的连接,accept()返回错误EAGAIN或EWOULDBLOCK。这里没处理,后面应该会有的。处理比如返回信号。
if ( (childpid = Fork()) == 0) { /* child process *///多并发,创建子进程去处理
Close(listenfd); /* close listening socket */
//子进程不需要监听了,就只处理当前连接客户端就可以
str_echo(connfd); /* process the request */
exit(0);
}
//父进程继续监听,但是不需要接受数据。
Close(connfd); /* parent closes connected socket */
}
}
#include "unp.h"
void
str_echo(int sockfd)
{
ssize_t n;
char buf[MAXLINE];
again://标签
//read()函数返回值为0,表示正常结束,已读完文件;
//返回值为-1,如果全局变量errono = EINT表示中断,需要重新再读。否则读取错误,结束
while ( (n = read(sockfd, buf, MAXLINE)) > 0)
Writen(sockfd, buf, n);//写进写缓冲区,发送给客户端。
//这里就是处理没有正常结束的情况的。
if (n < 0 && errno == EINTR)
goto again;
else if (n < 0)
err_sys("str_echo: read error");
}
客户端
#include "unp.h"
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);
//argv[1]是127.0.0.1之类的表达式,通过这个函数转化为需要的二进制数。
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));//客户端两部曲②
str_cli(stdin, sockfd); /* do it all */
exit(0);
}
#include "unp.h"
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);
}
}
配置运行
网上博客很多都不对,至少对我的很不适用。
首先先按照这篇博客进行配置 http://blog.sina.com.cn/s/blog_beb8b5d70101dq1a.html
如果编译中出现inet_ntop的size不匹配,就打开相关文件把size_t 改成socklen_t。
然后在看这篇博客:http://blog.sina.com.cn/s/blog_a43aba560101a7gf.html
因为我们的客户端、服务器的程序都在一个目录下的,
所以只要看这几句就可以了:
gcc -o tcpserv01 tcpserv01.c -lunp //(在终端1中输入,因为客户端服务器是两个进程)
gcc -o tcpcli01 tcpcli01.c -lunp //(在终端2输入)
./tcpserv01 //(在终端1)
./tcpcli01 127.0.0.1 //(在终端2)