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

      服务器端可以接收多个客户端的连接请求,并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. 浅谈getAttribute兼容性

    最近终于证实tag.setAttribute("style", "color:#000;");在IE7中不起作用.于是百度了一些解决办法. IE的setAttr ...

  2. codeforces 126B

    Asterix, Obelix and their temporary buddies Suffix and Prefix has finally found the Harmony temple. ...

  3. Asp.net MVC 使用PagedList(新的已更名 为X.PagedList.Mvc) 分页

    在asp.net mvc 中,可以bootstrap来作为界面,自己来写分页程序.也可以使用PagedList(作者已更名为 X.PagedList.Mvc)来分页. 1.首先,在NuGet程序包管理 ...

  4. JAVA基础--JAVA语言组成01

      2.   标识符 2.1.   定义: 就是用来起到 标识作用的符号: (就是程序员对自己定义的东西起的名字) 2.2.   命名规则(语法规定的,必须遵守的): 1.可以由大小写字母.数字.下划 ...

  5. 详解Supervisor进程守护监控

    Supervisor在百度百科上给的定义是超级用户,监管员.Supervisor是一个进程管理工具,当进程中断的时候Supervisor能自动重新启动它.可以运行在各种类unix的机器上,superv ...

  6. https://www.luogu.org/blog/An-Amazing-Blog/mu-bi-wu-si-fan-yan-ji-ge-ji-miao-di-dong-xi

    https://www.luogu.org/blog/An-Amazing-Blog/mu-bi-wu-si-fan-yan-ji-ge-ji-miao-di-dong-xi

  7. 51nod 1095【映射】

    思路: 利用一个map记录初始的,利用一个map记录排序后的. #include <bits/stdc++.h> using namespace std; map<string,in ...

  8. 鉴于spfa基础上的差分约束算法

    怎么搞?        1. 如果要求最大值      想办法把每个不等式变为标准x-y<=k的形式,然后建立一条从y到x权值为k的边,变得时候注意x-y<k =>x-y<=k ...

  9. springboot整合H2内存数据库,实现单元测试与数据库无关性

    一.新建spring boot工程 新建工程的时候,需要加入JPA,H2依赖 二.工程结构   pom文件依赖如下: <?xml version="1.0" encoding ...

  10. Angular 项目开发中父子组件传参

    在项目开发中经常会遇到 组件之间传参的问题.今天总结下在使用angular的项目中父子组件传参的问题: 1.父组件向子组件传参: 然后在父组件中 然后在父组件的html中 然后就可以在子组件中使用了 ...