TCP C/S 示例详解
2014-05-04 09:34
183 查看
下面的代码实现 echo 回射功能,下面具体分析启动与终止过程。
#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;
servaddr.sin_port=htosn(SERV_PORT);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
Bind(listenfd,(SA*)&servaddr,sizeof(servaddr));
Listen(listenfd,LISTENQ);
for(;;)
{
clilen=sizeof(cliaddr);
connfd=Accept(listenfd,(SA*)&cliaddr,&clilen);
if((childpi=fork())==0)
{
Close(listenfd);
str_echo(connfd);
exit(0);
}
Close(connfd);
}
}
void str_echo(int connfd)
{
ssize_t n;
char buf[MAXLINE];
again:
while((n=read(connfd,buf,MAXLINE))>0)
Write(connfd,buf,n);
if(n<0&&errno==EINTR)
goto again; // 这里EINTR表示是系统调用中断,若是这种情况
// 则重新调用系统调用。
//
else if(n<0)
err_sys("str_echo:read error");
}
//////////////////////////////////////////////
//client.c
int main(int argc,char**argv)
{
int sockfd;
struct sockaddr_in servaddr;
if(argc!=2)
err_quit("usage:tcpcli<IPaddress>");
sockfd=Socket(AF_IENT,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,(SA*)&servaddr,sizeof(servaddr));
str_cli(stdin,sockfd);
exit(0);
}
void str_cli(FILE*fp,int sockfd)
{
char sendline[MAXLINE],recvline[MAXLINE];
while(Fgets(sendline,MAXLIEN,fp)!=NULL)
{
Write(sockfd,sendline,strlen(sendline));
if(Read(sockfd,recvline,MAXLINE)==0)
err_quit("str_cli:server terimnate prematily");
Fputs(recvline,stdout);
}
}
1,首先,服务器启动,它调用socket,bind ,listen,accept,并阻塞与accept调用,我们还没有启动客户。此时,查看套接字情况netstat
有一个套接字处于LISTEN的状态,
2,然后,启动客户,客户调用socket和connect,后者引发三次握手过程,当三路握手完成后,客户中的connect和accept均返回,于是链接建立。
a,客户调用str_cli函数,该函数阻塞与fgets调用。
b, 服务器accept返回后,调用fork,再由子进程调用str_echo。该函数阻塞与read调用,等待读入数据。
c, 另一方面,服务器父进程再次调用accept并阻塞,等待下一个客户连接。
自此,有三个进程都在眠,客户进程,服务器父进程,服务器子进程。
当三路握手完成后,我们首先列出客户的步骤,然后列出服务器的步骤,是有原因的:客户接受到三路握手的第二个分节(ack+syn)connect就返回了,而服务器要等到三路
握手的第三个分节才返回(ack)即在connect返回之后再过半个RTT才返回。
此时,再次查看套接字状态,可以看到,有两个ESTABLIED,一个LISTEN 态,(因为我们是在同一主机运行客户与服务器的)。
终止:
在客户端,我们键入终端字符EOF字符(ctrl+D)以终止客户,此时,如果立即执行netstat命令(立即,因为过段时间,套接字状态会有变化)。
可以看到,当前链接的客户端套接字进入了TIME_WAIT状态,服务器父进程仍在LISTEN状态。
可以总结出:
1,当我们键入EOF字符释,fgets返回一个空指针,于是str_cli函数返回。
2,当str_cli返回到客户的main函数时候,main函数通过exit终止。
3,进程终止处理的部分工作是关闭所有打开的描述符,当然包括套接字,因此客户打开的套接字由内核关闭。这导致客户tcp发送一个FIN分节给服务器,服务器则ACK响应,
这就是TCP连接终止序列的前半部分。(注意到,这时候的服务器子进程还是存在的),自此,服务器子进程套接字处于CLOSE_WAIT状态,客户套接字则处于FIN_WAIT_2
状态(接受了ACK分节后从FIN_WAIT_1变迁的).
4, 当服务器紫禁城TCP接受到FIN时候,服务器紫禁城阻塞于read调用,于是read返回0,这导致str_echo函数返回服务器子进程的main函数。
5,服务器子进程通过调用exit(0)来终止
5,服务器子进程打开的所有描述符随之关闭。由子进程来关闭已链接套接字会引发TCP链接终止的后两个分节:一个从服务器端发出的FIN后从客户端到服务器的ACK。
自此,连接完全终止,客户套接字进入TIME_WAIT状态(需要等待一段时间才关闭),服务器子进程套接字关闭了。
6,进程终止处理的另一个部分:在服务器子进程终止时,给父进程发送一个SIGCHLD信号。这一点在本例中发生了,但是我们没有捕获处理,既然附近成没有处理
那么紫禁城于是进入僵死状态。 这可以用ps命令看到。
玩
#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;
servaddr.sin_port=htosn(SERV_PORT);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
Bind(listenfd,(SA*)&servaddr,sizeof(servaddr));
Listen(listenfd,LISTENQ);
for(;;)
{
clilen=sizeof(cliaddr);
connfd=Accept(listenfd,(SA*)&cliaddr,&clilen);
if((childpi=fork())==0)
{
Close(listenfd);
str_echo(connfd);
exit(0);
}
Close(connfd);
}
}
void str_echo(int connfd)
{
ssize_t n;
char buf[MAXLINE];
again:
while((n=read(connfd,buf,MAXLINE))>0)
Write(connfd,buf,n);
if(n<0&&errno==EINTR)
goto again; // 这里EINTR表示是系统调用中断,若是这种情况
// 则重新调用系统调用。
//
else if(n<0)
err_sys("str_echo:read error");
}
//////////////////////////////////////////////
//client.c
int main(int argc,char**argv)
{
int sockfd;
struct sockaddr_in servaddr;
if(argc!=2)
err_quit("usage:tcpcli<IPaddress>");
sockfd=Socket(AF_IENT,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,(SA*)&servaddr,sizeof(servaddr));
str_cli(stdin,sockfd);
exit(0);
}
void str_cli(FILE*fp,int sockfd)
{
char sendline[MAXLINE],recvline[MAXLINE];
while(Fgets(sendline,MAXLIEN,fp)!=NULL)
{
Write(sockfd,sendline,strlen(sendline));
if(Read(sockfd,recvline,MAXLINE)==0)
err_quit("str_cli:server terimnate prematily");
Fputs(recvline,stdout);
}
}
1,首先,服务器启动,它调用socket,bind ,listen,accept,并阻塞与accept调用,我们还没有启动客户。此时,查看套接字情况netstat
有一个套接字处于LISTEN的状态,
2,然后,启动客户,客户调用socket和connect,后者引发三次握手过程,当三路握手完成后,客户中的connect和accept均返回,于是链接建立。
a,客户调用str_cli函数,该函数阻塞与fgets调用。
b, 服务器accept返回后,调用fork,再由子进程调用str_echo。该函数阻塞与read调用,等待读入数据。
c, 另一方面,服务器父进程再次调用accept并阻塞,等待下一个客户连接。
自此,有三个进程都在眠,客户进程,服务器父进程,服务器子进程。
当三路握手完成后,我们首先列出客户的步骤,然后列出服务器的步骤,是有原因的:客户接受到三路握手的第二个分节(ack+syn)connect就返回了,而服务器要等到三路
握手的第三个分节才返回(ack)即在connect返回之后再过半个RTT才返回。
此时,再次查看套接字状态,可以看到,有两个ESTABLIED,一个LISTEN 态,(因为我们是在同一主机运行客户与服务器的)。
终止:
在客户端,我们键入终端字符EOF字符(ctrl+D)以终止客户,此时,如果立即执行netstat命令(立即,因为过段时间,套接字状态会有变化)。
可以看到,当前链接的客户端套接字进入了TIME_WAIT状态,服务器父进程仍在LISTEN状态。
可以总结出:
1,当我们键入EOF字符释,fgets返回一个空指针,于是str_cli函数返回。
2,当str_cli返回到客户的main函数时候,main函数通过exit终止。
3,进程终止处理的部分工作是关闭所有打开的描述符,当然包括套接字,因此客户打开的套接字由内核关闭。这导致客户tcp发送一个FIN分节给服务器,服务器则ACK响应,
这就是TCP连接终止序列的前半部分。(注意到,这时候的服务器子进程还是存在的),自此,服务器子进程套接字处于CLOSE_WAIT状态,客户套接字则处于FIN_WAIT_2
状态(接受了ACK分节后从FIN_WAIT_1变迁的).
4, 当服务器紫禁城TCP接受到FIN时候,服务器紫禁城阻塞于read调用,于是read返回0,这导致str_echo函数返回服务器子进程的main函数。
5,服务器子进程通过调用exit(0)来终止
5,服务器子进程打开的所有描述符随之关闭。由子进程来关闭已链接套接字会引发TCP链接终止的后两个分节:一个从服务器端发出的FIN后从客户端到服务器的ACK。
自此,连接完全终止,客户套接字进入TIME_WAIT状态(需要等待一段时间才关闭),服务器子进程套接字关闭了。
6,进程终止处理的另一个部分:在服务器子进程终止时,给父进程发送一个SIGCHLD信号。这一点在本例中发生了,但是我们没有捕获处理,既然附近成没有处理
那么紫禁城于是进入僵死状态。 这可以用ps命令看到。
玩
相关文章推荐
- cpio 命令用法详解示例
- c/c++中define用法详解及代码示例
- Spring AOP详解(示例)
- C语言指针详解及用法示例
- OWC详解,以及OWC生成柱图,线图,饼图示例
- 详解Bucket Sort桶排序算法及C++代码实现示例
- 三层交换原理及示例详解 推荐
- layout_weight详解示例
- C++中各种初始化方式示例详解
- Log4J基础详解及示例
- 用示例详解php连接数据库操作
- linux获取系统启动时间示例详解
- IOS之sha加密、md5常规加密、md5二次加密详解及示例程序
- Hbase 过滤器详解及一些代码测试示例
- android JNI的使用示例详解
- Java:集合框架详解(ArrayList)和代码示例
- Android多线程下载示例详解
- php示例详解Constructor Prototype Pattern 原型模式
- C#中参数数组、引用参数和输出参数示例详解
- socket编程中的超时设置示例详解之一