前面几篇中实现的client每次运行只能从命令行读取一个字符串发给服务器,再从服务器收回来,现在我们把它改成交互式的,不断从终端接受用户输入并和server交互。

/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include "wrap.h" #define MAXLINE 80
#define SERV_PORT 8000 int main(int argc, char *argv[])
{
structsockaddr_in servaddr;
charbuf[MAXLINE];
intsockfd, n; sockfd= Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family= AF_INET;
inet_pton(AF_INET,"127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port= htons(SERV_PORT); Connect(sockfd,(struct sockaddr *)&servaddr, sizeof(servaddr)); while(fgets(buf, MAXLINE, stdin) != NULL) {
Write(sockfd,buf, strlen(buf));
n= Read(sockfd, buf, MAXLINE);
if(n == 0)
printf("theother side has been closed.\n");
else
Write(STDOUT_FILENO,buf, n);
} Close(sockfd);
return0;
}

编译并运行server和client,看看是否达到了你预想的结果。

这时server仍在运行,但是client的运行结果并不正确。原因是什么呢?仔细查看server.c可以发现,server对每个请求只处理一次,应答后就关闭连接,client不能继续使用这个连接发送数据。但是client下次循环时又调用write发数据给server,write调用只负责把数据交给TCP发送缓冲区就可以成功返回了,所以不会出错,而server收到数据后应答一个RST段,client收到RST段后无法立刻通知应用层,只把这个状态保存在TCP协议层。client下次循环又调用write发数据给server,由于TCP协议层已经处于RST状态了,因此不会将数据发出,而是发一个SIGPIPE信号给应用层,SIGPIPE信号的缺省处理动作是终止程序,所以看到上面的现象。

为了避免client异常退出,上面的代码应该在判断对方关闭了连接后break出循环,而不是继续write。另外,有时候代码中需要连续多次调用write,可能还来不及调用read得知对方已关闭了连接就被SIGPIPE信号终止掉了,这就需要在初始化时调用sigaction处理SIGPIPE信号,如果SIGPIPE信号没有导致进程异常退出,write返回-1并且errno为EPIPE。

另外,我们需要修改server,使它可以多次处理同一客户端的请求。

/* server.c */
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include "wrap.h" #define MAXLINE 80
#define SERV_PORT 8000 int main(void)
{
structsockaddr_in servaddr, cliaddr;
socklen_tcliaddr_len;
intlistenfd, connfd;
charbuf[MAXLINE];
charstr[INET_ADDRSTRLEN];
inti, n; 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,20); printf("Acceptingconnections ...\n");
while(1) {
cliaddr_len= sizeof(cliaddr);
connfd= Accept(listenfd,
(structsockaddr *)&cliaddr, &cliaddr_len);
while(1) {
n= Read(connfd, buf, MAXLINE);
if(n == 0) {
printf("theother side has been closed.\n");
break;
}
printf("receivedfrom %s at PORT %d\n",
inet_ntop(AF_INET,&cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port)); for(i = 0; i < n; i++)
buf[i]= toupper(buf[i]);
Write(connfd,buf, n);
}
Close(connfd);
}
}

经过上面的修改后,客户端和服务器可以进行多次交互了。

Linux系统编程(34)—— socket编程之TCP服务器与客户端的交互的更多相关文章

  1. Linux系统编程(32)—— socket编程之TCP服务器与客户端

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

  2. Linux系统编程(37)—— socket编程之UDP服务器与客户端

    典型的UDP客户端/服务器通讯过程: 编写UDP Client程序的步骤 1.初始化sockaddr_in结构的变量,并赋值.这里使用"8888"作为连接的服务程序的端口,从命令行 ...

  3. Linux系统编程(35)—— socket编程之TCP服务器的并发处理

    我们知道,服务器通常是要同时服务多个客户端的,如果我们运行上一篇实现的server和client之后,再开一个终端运行client试试,新的client就不能能得到服务了.因为服务器之支持一个连接. ...

  4. C#编程 socket编程之tcp服务器端和客户端

    基于Tcp协议的Socket通讯类似于B/S架构,面向连接,但不同的是服务器端可以向客户端主动推送消息. 使用Tcp协议通讯需要具备以下几个条件: (1).建立一个套接字(Socket) (2).绑定 ...

  5. linux socket编程之TCP与UDP

    转:http://blog.csdn.net/gaoxin1076/article/details/7262482 TCP/IP协议叫做传输控制/网际协议,又叫网络通信协议 TCP/IP虽然叫传输控制 ...

  6. (54)LINUX应用编程和网络编程之九Linux网络通信实践

    3.9.1.linux网络编程框架 3.9.1.1.网络是分层的 (1)OSI 7层模型(理论指导) (2)网络为什么要分层 (3)网络分层的具体表现 3.9.1.2.TCP/IP协议引入(网络分层实 ...

  7. Linux系统编程:socket网络编程(操作篇)

    一.问题思考 问1.网络通信应用在什么场合?通信的前提是什么? 答1.主要应用在不同主机进程间的互相通信,同一主机的进程也可以使用网络进行通信.通信的前提是如何标识通信进程的唯一,由于不同主机的进程极 ...

  8. (47)LINUX应用编程和网络编程之二Linux文件属性

    Linux下的文件系统为树形结构,入口为/ 树形结构下的文件目录: 无论哪个版本的Linux系统,都有这些目录,这些目录应该是标准的.各个Linux发行版本会存在一些小小的差异,但总体来说,还是大体差 ...

  9. Socket 编程之 TCP 实现

    前几天介绍了计算机网络的一些概念,并介绍了几个协议.下面就说说 Java 中的 Socket 编程,服务器和客户端是如何通信的呢? 首先要介绍一下 Socket ,我们知道在 TCP/IP 协议簇中, ...

随机推荐

  1. localStorage存储JSON对象的小方法

    有时候,我们需要将数据存储到sessionStorage和localStorage中,这样做的好处有: 1 缓存数据 2 减少对内存的占用 但是,storage只能存储字符串的数据,对于JS中常用的数 ...

  2. Object-C 点语法 -- 笔记

    第一种是经典方式, 第一种是点语法.

  3. redis常见错误

    1.Redis Error --MISCONF Redis is configured to save RDB snapshots省略 分析:(linux)未用root启动,用的app用户(没有最高权 ...

  4. 01标题背包水章 HDU2955——Robberies

    原来是dp[i],它代表的不被抓的概率i这最大的钱抢(可能1-100) 客是dp[i]表示抢了i钱最大的不被抓概率,嗯~,弱菜水题都刷不动. 那么状态转移方程就是 dp[i]=max(dp[i],dp ...

  5. Haskell之Yesod开发–边踩坑边开发(2)

    今天继续上一节的开发 今天我们须要详细的开发一个图书馆站点,分为下面几个页面 / HomeR GET 主页 /login LoginR GET 用户登录页面 /library LibraryR GET ...

  6. wpf异常:指定的 Visual 不是此 Visual 的上级问题处理解析

    WPF在画线的时候,调用Control0.TransformToAncestor(Control1).Transform(new System.Windows.Point(0, 0))方法转换坐标的时 ...

  7. jQuery源代码 解析一 工具方法

    1. 外层沙箱以及命名空间$ 几乎稍微有点经验前端人员都这么做,为了避免声明了一些全局变量而污染,把代码放在一个"沙箱执行",然后在暴露出命名空间(可以为API,函数,对象): 2 ...

  8. Windows与自定义USB HID设备通信说明.

    1 .   所使用的典型 Windows API CreateFile ReadFile WriteFile 以下函数是 DDK 的内容: HidD_SetFeature HidD_GetFeatur ...

  9. 3_Linux_文件搜索指令

    .3文件搜索命令 1)which 查找一个命令所在的路径 whereis 提供命令的帮助文件的信息 whatis 显示命令的概要信息whatis ls which提供命令的别名信息 2)find,基本 ...

  10. PV和并发

    几个概念 网站流量是指网站的访问量,用来描述访问网站的用户数量以及用户所浏览的网页数量等指标,常用的统计指标包括网站的独立用户数量.总用户数量(含重复访问者).网页浏览数量.每个用户的页面浏览数量.用 ...