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

      服务器端可以接收多个客户端的连接请求,并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. apple-touch-startup-image 制作iphone web应用程序的启动画面

    为ipad制作web应用程序的启动画面时发现个问题,只能显示竖屏图,横屏图出不来,如下: 首先页面头部里要加入(这个是APP启动画面图片,如果不设置,启动画面就是白屏,图片像素就是手机全屏的像素) & ...

  2. pl/sql developer安装使用即时客户端

    pl/sql developer安装使用即时客户端 背景:由于Oracle的安装比较麻烦,大部分人都使用远程Oracle,而Oracle Instantclient Basic package可以解决 ...

  3. windows server安装zabbix-agent

    1.准备安装包: 下载链接:https://www.zabbix.com/downloads/3.4.0/zabbix_agents_3.4.0.win.zip 2.在C盘下创建一个zabbix目录, ...

  4. redis简介、安装、配置和数据类型

    redis简介.安装.配置和数据类型 redis简介 Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理. 它支持字符串.哈希表.列表.集合.有序集合, ...

  5. 博客使用的CSS代码备份

    CSS代码备份 /*simplememory*/ #google_ad_c1, #google_ad_c2 { display: none; } .syntaxhighlighter a, .synt ...

  6. bzoj 4403: 序列统计【lucas+组合数学】

    首先,给一个单调不降序列的第i位+i,这样就变成了单调上升序列,设原来数据范围是(l,r),改过之后变成了(l+1,r+n) 在m个数里选长为n的一个单调上升序列的方案数为\( C_m^n \),也就 ...

  7. 第三篇 .NET高级技术之深拷贝和浅拷贝

    深拷贝.浅拷贝 如果拷贝的时候共享被引用的对象就是浅拷贝,如果被引用的对象也拷贝一份出来就是深拷贝.(深拷贝就是说重新new一个对象,然后把之前的那个对象的属性值在重新赋值给这个用户) using S ...

  8. (四)SpringBoot如何定义消息转换器

    一:添加fastjson依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastj ...

  9. Ubuntu还是windows呢

    本来想把才换不久的电脑也换成Ubuntu,犹豫再三,还是把这个老电脑作为Ubuntu的主力机把,毕竟大屏幕看着也得劲 新电脑还是win10吧,毕竟现在速度还是刷刷的,等过几年速度降下来了,就换成Ubu ...

  10. AtCoder Regular Contest 062 E - AtCoDeerくんと立方体づくり / Building Cubes with AtCoDeer

    题目传送门:https://arc062.contest.atcoder.jp/tasks/arc062_c 题目大意: 给你\(N\)块正方形木板,每块木板四角有四种颜色(可以相同),木板中央有编号 ...