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

      服务器端可以接收多个客户端的连接请求,并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. bzoj3653

    主席树+dfs序 b在a上方时可以O(1)算出来,子树中就用主席树查询区间和,权值线段树的下标是深度,值是子树size-1,每次查询就行了...线段树合并挂了 #include<bits/std ...

  2. UVa11077

    dp+置换 可以把排列分成几个循环,然后dp统计 dp[i][j]=dp[i-1][j-1]*(i-1)+dp[i-1][j],表示当前有i个元素,至少换j次,然后如果不在自己应该在的位置有i-1种情 ...

  3. VMware ESXI虚拟机挂载移动硬盘

    Windows server2008 R2 1.“编辑虚拟机设置”,点击“添加” 2.点击添加“USB控制器”: 3.添加完“USB控制器”以后,再点击添加“USB设备”: 完成即可:如果系统里面不显 ...

  4. HDU 5878 I Count Two Three (预处理+二分查找)

    题意:给出一个整数nnn, 找出一个大于等于nnn的最小整数mmm, 使得mmm可以表示为2a3b5c7d2^a3^b5^c7^d2​a​​3​b​​5​c​​7​d​​. 析:预处理出所有形为2a3 ...

  5. python mixin到底是什么 django

    1.什么是Mixin 在面向对象编程中,Mixin是一种类,这种类包含了其他类要使用的方法,但不必充当其他类的父类.其他类是如何获取Mixin中的方法因语言的不同而不同.所以有时候Mixin被描述为' ...

  6. 二分匹配ZOJ3646

    //题意:类比线代里:把矩阵中的U看作[1],是否满足一个满秩矩阵 //利用二分匹配就是 //每一行都有相对应的列: #include<iostream> #include<stri ...

  7. 51单片机 小车 L298N pwm调速 串口控制 按键控制

    难点:1.串口定时器T1,和T0定时器优先级 2.pwm频率与占空比的设置 按键控制 按键1——前进 按键2——后退 按键3——加速 按键4——减速 (板子上只有四个按键) 串口控制 ‘1’——前进 ...

  8. CF487E Tourists【圆方树+tarjan+multiset+树剖+线段树】

    圆方树不仅能解决仙人掌问题(虽然我仙人掌问题也没用过圆方树都是瞎搞过去的),还可以解决一般图的问题 一般图问题在于缩完环不是一棵树,所以就缩点双(包括双向边) 每个方点存他所在点双内除根以外的点的最小 ...

  9. IT兄弟连 JavaWeb教程 Servlet中定义的变量的作用域类型

    在Java语言中,局部变量和实力变量有着不同的作用于,它们的区别如下: 局部变量在一个方法中定义,每当一个线程执行局部变量所在的方法时,在线程的堆栈中就会创建这个局部变量,当线程执行完该方法,局部变量 ...

  10. 第十二篇 .NET高级技术之lambda表达式

    最近由于项目需要,刚刚学完了Action委托和Func<T>委托,发现学完了委托就必须学习lambda表达式,委托和Lambda表达式联合起来,才能充分的体现委托的便利.才能使代码更加简介 ...