上一节给出了TCP网络编程的函数。这一节使用那些基本函数编写一个完毕的TCP客户/server程序演示样例。

该样例运行的过程例如以下:

1、客户从标准输入读入一行文本,并写给server。

2、server从网络输入读入这行文本,并回射给客户。

3、客户从网络输入读入这行回射文本,并显示在标准输出上。

用图描写叙述例如以下:

编写TCP回射server程序例如以下:

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> #define SERV_PORT 9877
#define MAXLINE 2048 void
str_echo(int sockfd)
{
ssize_t n;
char buf[MAXLINE]; again:
while((n = read(sockfd, buf, MAXLINE)) > 0)
write(sockfd, buf, n); if(n < 0 && errno == EINTR)
goto again;
else if(n < 0)
printf("str_echo : read error");
} 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, IPPROTO_TCP);
if(listenfd == -1){
printf("socket fail.\n");
return -1;
} bzero(&servaddr, sizeof(struct sockaddr_in));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr))){
printf("bind fail.\n");
return -1;
}
if(listen(listenfd, 5)){
printf("listen fail.\n");
return -1;
} printf("listen stat.\n"); for(;;){
clilen = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
if(connfd == -1){
printf("accept fail.\n");
return -1;
}
printf("accept stat.\n");
if((childpid = fork()) == 0){
close(listenfd);
printf("servers.\n");
str_echo(connfd);
exit(0);
}
close(connfd);
} return 0;
}

TCP回射客户程序例如以下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h> #define MAXLINE 2048
#define SERV_PORT 9877 void str_cli(FILE *, int); int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in cliaddr; if(argc != 2){
printf("usage:tcpcli <IPaddress>");
return -1;
} sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd == -1){
printf("socket fail.\n");
return -1;
} bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(SERV_PORT);
if(!(inet_pton(AF_INET, argv[1], &cliaddr.sin_addr))){
printf("inet pton fail.\n");
return -1;
} if(connect(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr))){
printf("connect fail.\n");
return -1;
} str_cli(stdin, sockfd); exit(0); } void
str_cli(FILE *fp, int sockfd)
{
char sendline[MAXLINE], recvline[MAXLINE]; while(fgets(sendline, MAXLINE, fp) != NULL){
write(sockfd, sendline, strlen(sendline)); if(read(sockfd, recvline, MAXLINE) == 0)
printf("str_cli:server terminated prematurely"); fputs(recvline, stdout);
}
}

编译两个程序:

gcc tcpserv.c -o tcpserv

gcc tcpcli.c -o tcpcli

測试,正常启动server./tcpserv &,之后启动client./tcpcli 127.0.0.1,之后就能够在终端输入一行文本,接着就会显示出该文本。

分析整个建立过程:

1、首先在后台启动server,server启动后,它调用socket、bind、listen和accept。并堵塞于accept调用。在启动客户程序之前。用netstat程序检查server监听套接字的状态。

netstat -a | grep 9877。显演示样例如以下内容:

tcp        0      0 *:9877                  *:*                     LISTEN

该行表明。有一个套接字处于LISTEN状态,它有通配的本地IP地址。本地端口为9877。

2、接着在同一主机上启动客户。并指定server主机的IP地址为127.0.0.1(环回地址)。./tcpcli 127.0.0.1

客户调用socket和connect,connect引起TCP的三路握手过程。

当三路握手完毕之后。客户中的connect和server中的accept均返回,连接于是建立。

3、客户调用str_cli函数,该函数堵塞与fgets调用。

4、当server中的accept返回时,server调用fork。再又子进程调用str_echo。该函数调用read。而read在等待客户送入一行文本期间堵塞。

5、还有一方面。server父进程再次调用accept并堵塞,等待下一个客户连接。

至此。有3个都在睡眠的进程:客户进程、server父进程和server子进程。

分析终止过程:

连接建立后,在客户的标准输入中键入什么。都会回射到它的标准输出中,在客户正常终止时。客户和server的过程例如以下:

1、当我们键入EOF字符时,fgets返回一个空指针,于是str_cli函数返回。

2、当str_cli返回到客户的main函数时,main通过调用exit终止。

3、进程终止处理的部分工作是关闭全部打开的描写叙述符,因此客户打开的套接字由内核关闭。这导致客户TCP发送一个FIN给server,serverTCP则以ACK响应,这就是TCP链接终止序列的前半部分。

至此。server套接字处于CLOSE_WAIT状态,客户套接字则处于FIN_WAIT_2状态。

4、当serverTCP接收FIN时,server子进程堵塞于read调用,于是read返回0。这导致str_echo函数返回server子进程的main函数。

5、server子进程通过调用exit来终止。

6、server子进程中打开的全部描写叙述符随之关闭。

7、进程终止处理的还有一部分是:在server子进程终止时,给父进程发送一个SIGCHLD信号。

思考:

1、子进程给父进程发送了一个SIGCHLD信号,但父进程没有捕获它,而是採用了默认行为。这样会导致子进程进入僵死状态。

2、上面的都是正常启动正常终止的情况,若server进程在客户之前终止。则客户会发生什么?若server主机崩溃又会如何?等等

【UNIX网络编程(三)】TCP客户/server程序演示样例的更多相关文章

  1. 【Unix 网络编程】TCP 客户/服务器简单 Socket 程序

    建立一个 TCP 连接时会发生下述情形: 1. 服务器必须准备好接受外来的连接.这通常通过调用 socket.bind 和 listen 这三个函数来完成,我们称之为被动打开. 2. 客户通过调用 c ...

  2. MapGuide应用程序演示样例——你好,MapGuide!

    图 3‑4显示了基于MapGuide的Web应用程序的开发流程,整个开发流程能够分为五个阶段.图中,矩形代表任务,椭圆形被任务使用的或被任务创建的实体,箭头代表数据流. 1) 载入文件类型的数据,配置 ...

  3. UNIX网络编程卷1 时间获取程序server TCP 协议相关性

    本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie 最初代码:  这是一个简单的时间获取server程序.它和时间获取程序client一道工作. ...

  4. 《UNIX网络编程》TCP客户端服务器例子

    最近在看<UNIX网络编程>(简称unp)和<Linux程序设计>,对于unp中第一个获取服务器时间的例子,实践起来总是有点头痛的,因为作者将声明全部包含在了unp.h里,导致 ...

  5. UNIX网络编程——解决TCP网络传输“粘包”问题

    当前在网络传输应用中,广泛采用的是TCP/IP通信协议及其标准的socket应用开发编程接口(API).TCP/IP传输层有两个并列的协议:TCP和UDP.其中TCP(transport contro ...

  6. UNIX网络编程——基本TCP套接字编程

    一.基于TCP协议的网络程序 下图是基于TCP协议的客户端/服务器程序的一般流程: 服务器调用socket().bind().listen()完成初始化后,调用accept()阻塞等待,处于监听端口的 ...

  7. UNIX网络编程——UDP回射服务器程序(初级版本)以及漏洞分析

    该函数提供的是一个迭代服务器,而不是像TCP服务器那样可以提供一个并发服务器.其中没有对fork的调用,因此单个服务器进程就得处理所有客户.一般来说,大多数TCP服务器是并发的,而大多数UDP服务器是 ...

  8. UNIX网络编程卷1 时间获取程序client TCP 使用非堵塞connect

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.当在一个非堵塞的 TCP 套接字(可使用 fcntl 把套接字变成非堵塞的)上调用 co ...

  9. UNIX网络编程卷1 时间获取程序server UDP 协议无关

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie /** * UDP 协议无关 调用 getaddrinfo 和 udp_server **/ ...

随机推荐

  1. myeclipse创建hibernate工程

    1.创建数据库: from blog http://www.cnblogs.com/zhaocundang/p/9061959.html 使用navicat mysql IDE: 创建数据库 book ...

  2. cookie是如何保存到客户端,又是如何发送到服务端

    Cookie相关的Http头     有 两个Http头部和Cookie有关:Set-Cookie和Cookie.     Set-Cookie由服务器发送,它包含在响应请求的头部中.它用于在客户端创 ...

  3. duilib进阶教程 -- TreeView控件(6)

    代码下载:http://download.csdn.net/detail/qq316293804/6483905 上一个教程中,界面已经和迅雷一模一样啦,大小和位置一个像素都不差哟,亏得Alberl调 ...

  4. 大杂烩 -- HashMap、HashTable、ConCurrentHashMap 联系与区别

    基础大杂烩 -- 目录 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1. Hashtable 和 HashMap ⑴ ...

  5. Kafka ACL使用实战

    自0.9.0.0.版本引入Security之后,Kafka一直在完善security的功能.当前Kafka security主要包含3大功能:认证(authentication).信道加密(encry ...

  6. (转)mysql百万级以上查询优化

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...

  7. MFC接收命令行参数的三种方法

    方法一: CString sCmdline = ::GetCommandLine(); AfxMessageBox(sCmdline); 将获取到 "C:\test\app.exe -1 - ...

  8. 字符集和编码——Unicode(UTF&UCS)深度历险

    计算机网络诞生后,大家慢慢地发现一个问题:一个字节放不下一个字符了!因为需要交流,本地化的文字需要能够被支持. 最初的字符集使用7bit来存储字符,因为那时只需要存下一些英文字母和符号.后来虽然扩展到 ...

  9. SVM 核方法

    在 SVM 中引入核方法便可使得 SVM 变为非线性分类器,给定非线性可分数据集 $\left \{ (x_i,y_i)\right\}_{i=1}^N$,如下图所示,此时找不到一个分类平面来将数据分 ...

  10. zouxy09-图像卷积与滤波的一些知识点

    原文地址 图像卷积与滤波的一些知识点 zouxy09@qq.com http://blog.csdn.net/zouxy09 之前在学习CNN的时候,有对卷积进行一些学习和整理,后来就烂尾了,现在稍微 ...