实现的功能:一次只能读取一行,客户端输入之后,一回车,马上字符串传到服务器端并显示在终端,然后服务器端将字符串又传回给客户端。

      服务器端可以接收多个客户端的连接请求,并fork一个子进程来进行服务。

(1)封装一个只能访问套接字描述符的readline函数

(2)服务器端启动SO_REUSEADDR套接字选项,以便服务器端不必等待TIME_WAIT状态

这是服务器端代码:

 #include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE);\
}while() void do_service(int conn)
{
char recvbuf[];
while()
{
memset(recvbuf,,sizeof(recvbuf));
int ret=readline(conn,recvbuf,sizeof(recvbuf));
if(-==ret)
ERR_EXIT("readline in do_servece");
else if(==ret)
{
printf("client close\n");
break;
}
fputs(recvbuf,stdout);
writen(conn,recvbuf,strlen(recvbuf));
}
} ssize_t readn(int fd,void *buf,size_t count)
{
size_t nleft=count;
ssize_t nread;
char*bufp=(char*)buf;
while(nleft>)
{
if((nread=read(fd,bufp,nleft))<)
{
if(errno==EINTR)
continue;
return -;
}
else if(nread==)
return count-nleft;
bufp+=nread;
nleft-=nread;
}
return count;
} ssize_t writen(int fd,const void*buf,size_t count)
{
size_t nleft=count;
ssize_t nwritten;
char*bufp=(char*)buf;
while(nleft>)
{
if((nwritten=write(fd,bufp,nleft))<)
{
if(errno==EINTR)
continue;
return -;
}
else if(nwritten==)
continue;
bufp+=nwritten;
nleft-=nwritten;
}
return count;
} ssize_t recv_peek(int sockfd,void *buf,size_t len)
{
while()
{
int ret=recv(sockfd,buf,len,MSG_PEEK);//窥看,不清除缓存中的数据
if(-==ret&&EINTR==errno)
continue;
return ret;
}
} ssize_t readline(int sockfd,void* buf,size_t maxline)
{
int ret;
int nread;
char* bufp=(char*)buf;
int nleft=maxline;
while()
{
ret=recv_peek(sockfd,bufp,nleft);
if(ret<)
return ret;
else if(==ret)
return ret;
nread=ret;
int i;
for(i=;i<nread;i++)//读取到'\n'时就应该结束
{
if(bufp[i]=='\n')
{
ret=readn(sockfd,bufp,i+);
if(ret!=i+)
exit(EXIT_FAILURE);
return ret;
}
}
//执行到了这儿,说明没有读取到'\n'
if(nread>nleft)
exit(EXIT_FAILURE);
nleft-=nread;
ret=readn(sockfd,bufp,nread);//清空缓存中的数据,因为之前是窥视,并没有清空缓存
if(ret!=nread)
exit(EXIT_FAILURE);
bufp+=nread;//指针后移,接着去执行while循环,确保数据追加到原来已读取数据的末尾
}
return -;
}
int main(void)
{
int listenfd;
if((listenfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<)
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr,,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons();
servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
//套接字选项的设置一定要在bind之前
int on=;
if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<)
ERR_EXIT("setsockopt");
if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<)
ERR_EXIT("bind");
if(listen(listenfd,SOMAXCONN)<)
ERR_EXIT("listen");
struct sockaddr_in peeraddr;
socklen_t peerlen=sizeof(peeraddr);
int conn;
pid_t pid;
while()
{
if((conn=accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen))<)
ERR_EXIT("accept");
printf("ip=%s port=%d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
pid=fork();
if(-==pid)
ERR_EXIT("fork");
if(==pid)//子进程
{
close(listenfd);
do_service(conn);
exit(EXIT_SUCCESS);
}
else close(conn);//父进程
} return ;
}

这是客户端代码:

 #include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE);\
}while() ssize_t readn(int fd,void *buf,size_t count)
{
size_t nleft=count;
ssize_t nread;
char*bufp=(char*)buf;
while(nleft>)
{
if((nread=read(fd,bufp,nleft))<)
{
if(errno==EINTR)
continue;
return -;
}
else if(nread==)
return count-nleft;
bufp+=nread;
nleft-=nread;
}
return count;
} ssize_t writen(int fd,const void*buf,size_t count)
{
size_t nleft=count;
ssize_t nwritten;
char*bufp=(char*)buf;
while(nleft>)
{
if((nwritten=write(fd,bufp,nleft))<)
{
if(errno==EINTR)
continue;
return -;
}
else if(nwritten==)
continue;
bufp+=nwritten;
nleft-=nwritten;
}
return count;
} ssize_t recv_peek(int sockfd,void *buf,size_t len)
{
while()
{
int ret=recv(sockfd,buf,len,MSG_PEEK);//窥看,不清除缓存中的数据
if(-==ret&&EINTR==errno)
continue;
return ret;
}
} ssize_t readline(int sockfd,void* buf,size_t maxline)
{
int ret;
int nread;
char* bufp=(char*)buf;
int nleft=maxline;
while()
{
ret=recv_peek(sockfd,bufp,nleft);
if(ret<)
return ret;
else if(==ret)
return ret;
nread=ret;
int i;
for(i=;i<nread;i++)//读取到'\n'时就应该结束
{
if(bufp[i]=='\n')
{
ret=readn(sockfd,bufp,i+);
if(ret!=i+)
exit(EXIT_FAILURE);
return ret;
}
}
//执行到了这儿,说明没有读取到'\n'
if(nread>nleft)
exit(EXIT_FAILURE);
nleft-=nread;
ret=readn(sockfd,bufp,nread);//清空缓存中的数据,因为之前是窥视,并没有清空缓存
if(ret!=nread)
exit(EXIT_FAILURE);
bufp+=nread;//指针后移,接着去执行while循环,确保数据追加到原来已读取数据的末尾
}
return -;
}
int main(void)
{
int sock;
if((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<)
ERR_EXIT("socket");
struct sockaddr_in servaddr;
memset(&servaddr,,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons();
servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");//inet_addr将ip地址字符串转为数字形式,且已经是网络字节序 if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr))<)//
ERR_EXIT("connect"); char sendbuf[]={};
char recvbuf[]={};
while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL)
{
writen(sock,sendbuf,strlen(sendbuf)); int ret=readline(sock,recvbuf,sizeof(recvbuf));
if(-==ret)
ERR_EXIT("readline");
else if(==ret)
{
printf("client?server close\n");
break;
}
fputs(recvbuf,stdout);
} return ;
}

自己封装一个readline函数实现服务器客户端回射的更多相关文章

  1. Socket 入门- 客户端回射程序

    结果输出:------------------------------------------------------客户端:xx@xxxxxx:~/Public/C$ ./postBackCli.o ...

  2. 使用原生JS封装一个动画函数

    最近一直在忙项目,很少有时间回顾之前的知识,今天刚好要做一个轮播,因为对兼容性有一定的要求,使用了各种插件和库中的轮播,效果都不是很理想,一怒之下,使用原生JS封装了一个轮播组件,其中重要的功能就是一 ...

  3. 使用promise对象封装一个ajaxGet函数

    function promiseAjax(url,data){        var pro = new Promise(function(success,failed){           承诺一 ...

  4. 自己封装一个MySignal函数,方便以后直接copy.

    传统的signal可能会有信号未决或者信号重入或多或少的问题,毕竟这个函数已经很多年了. 所以推荐使用sigaction函数,但是sigaction函数相对signal较为复杂,而且每次要写一大堆.因 ...

  5. TCP粘包问题的解决方案02——利用readline函数解决粘包问题

      主要内容: 1.read,write 与 recv,send函数. recv函数只能用于套接口IO ssize_t recv(int sockfd,void * buff,size_t len,i ...

  6. c++下基于windows socket的单线程服务器客户端程序(基于TCP协议)

    今天自己编写了一个简单的c++服务器客户端程序,注释较详细,在此做个笔记. windows下socket编程的主要流程可概括如下:初始化ws2_32.dll动态库-->创建套接字-->绑定 ...

  7. Java新AIO/NIO2:AsynchronousServerSocketChannel和AsynchronousSocketChannel简单服务器-客户端

    Java新AIO/NIO2:AsynchronousServerSocketChannel和AsynchronousSocketChannel简单服务器-客户端用AsynchronousServerS ...

  8. 网络编程readn、writen和readline函数的编写

    readn   在Linux中,read的声明为: ssize_t read(int fd, void *buf, size_t count); 它的返回值有以下情形: 1.大于0,代表成功读取的字节 ...

  9. TCP回射服务器程序:str_echo函数

    str_echo函数执行处理每个客户的服务: 从客户读入数据,并把它们回射给客户 读入缓冲区并回射其中内容: read函数从套接字读入数据,writen函数把其中内容回射给客户 如果客户关闭连接,那么 ...

随机推荐

  1. C# 利用Aspose.Words .dll将本地word文档转化成pdf(完美破解版 无水印 无中文乱码)

    下载Aspose.Words .dll  http://pan.baidu.com/s/1c8659k 在vs2010中新建窗体应用程序,命名为 wordtopdf 添加Aspose.Words .d ...

  2. (bmp格式)用CDialog的OnCtlColor()消息响应处理背景画刷。

    (bmp格式)用CDialog的OnCtlColor()消息响应处理背景画刷. 加载位图资源IDB_BITMAP1,在Dlg类头文件中加入: CBrush m_brush; 在OnInitDialog ...

  3. bzoj 3576: [Hnoi2014]江南乐【博弈论】

    这个东西卡常--预处理的时候要先把i%j,i/j都用变量表示,还要把%2变成&1-- 首先每一堆都是不相关子游戏,所以对于每一堆求sg即可 考虑暴力枚举石子数i,分割块数j,分解成子问题求xo ...

  4. 【POJ - 2664】Prerequisites? (排序+查找)

    Prerequisites? 原文是English,这里直接就写中文吧 题意简述 k:已经选择的科目数:m:选择的科目类别:c:能够选择的科目数.r:要求最少选择的科目数量 在输入的k和m以下的一行是 ...

  5. 普通app自动化测试与手游app自动化测试的区别

    [转载] 手游自动化测试与App自动化测试技术上的区别 手游和App的开发技术不同,导致了两者的自动化测试技术完全不同.一般来说,安卓应用是使用Android SDK开发的,利用Java编写.那么在A ...

  6. Django Views and URLconfs

    碧玉妆成一树高,万条垂下绿丝绦. 不知细叶谁裁出,二月春风似剪刀. 原文尽在:http://djangobook.com/ 转载请注明出处:http://www.cnblogs.com/A-FM/p/ ...

  7. spring MVC 文件上传错误

    1.The request sent by the client was syntactically incorrect () http://luanxiyuan.iteye.com/blog/187 ...

  8. Bits Equalizer UVA - 12545

    点击打开链接 #include<cstdio> #include<cstring> /* 别看错了:0能变1,1不能变0 能完成的条件是,s与t长度相等且s中0数量和?数量之和 ...

  9. Jenkins视图使用--添加删除视图

    job建立的特别多的时候,我们可能不太容易找到自己的某个job,这时,我们就可以在Jenkins中建立视图.job的视图类似于我们电脑上的文件夹.可以通过一些过滤规则,将已经建好的job过滤到视图中, ...

  10. oracle PL、SQL(概念)

    一.PL/SQL简介. Oracle PL/SQL语言(Procedural Language/SQL)是结合了结构化查询和Oracle自身过程控制为一体的强大语言,PL/SQL不但支持更多的数据类型 ...