自己封装一个readline函数实现服务器客户端回射

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

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

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

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

这是服务器端代码:

  1 #include<unistd.h>
  2 #include<sys/types.h>
  3 #include<sys/socket.h>
  4 #include<netinet/in.h>
  5 #include<arpa/inet.h>
  6 #include<stdlib.h>
  7 #include<stdio.h>
  8 #include<errno.h>
  9 #include<string.h>
 10
 11 #define ERR_EXIT(m)  12         do  13         {  14             perror(m);  15             exit(EXIT_FAILURE); 16         }while(0)
 17
 18 void do_service(int conn)
 19 {
 20     char recvbuf[1024];
 21     while(1)
 22     {
 23         memset(recvbuf,0,sizeof(recvbuf));
 24         int ret=readline(conn,recvbuf,sizeof(recvbuf));
 25         if(-1==ret)
 26             ERR_EXIT("readline in do_servece");
 27         else if(0==ret)
 28         {
 29             printf("client close\n");
 30             break;
 31         }
 32         fputs(recvbuf,stdout);
 33         writen(conn,recvbuf,strlen(recvbuf));
 34     }
 35 }
 36
 37 ssize_t readn(int fd,void *buf,size_t count)
 38 {
 39     size_t nleft=count;
 40     ssize_t nread;
 41     char*bufp=(char*)buf;
 42     while(nleft>0)
 43     {
 44         if((nread=read(fd,bufp,nleft))<0)
 45         {
 46             if(errno==EINTR)
 47                 continue;
 48             return -1;
 49         }
 50         else if(nread==0)
 51             return count-nleft;
 52         bufp+=nread;
 53         nleft-=nread;
 54     }
 55     return count;
 56 }
 57
 58 ssize_t writen(int fd,const void*buf,size_t count)
 59 {
 60     size_t nleft=count;
 61     ssize_t nwritten;
 62     char*bufp=(char*)buf;
 63     while(nleft>0)
 64     {
 65         if((nwritten=write(fd,bufp,nleft))<0)
 66         {
 67             if(errno==EINTR)
 68                 continue;
 69             return -1;
 70         }
 71         else if(nwritten==0)
 72             continue;
 73         bufp+=nwritten;
 74         nleft-=nwritten;
 75     }
 76     return count;
 77 }
 78
 79 ssize_t recv_peek(int sockfd,void *buf,size_t len)
 80 {
 81     while(1)
 82     {
 83         int ret=recv(sockfd,buf,len,MSG_PEEK);//窥看,不清除缓存中的数据
 84         if(-1==ret&&EINTR==errno)
 85             continue;
 86         return ret;
 87     }
 88 }
 89
 90 ssize_t readline(int sockfd,void* buf,size_t maxline)
 91 {
 92     int ret;
 93     int nread;
 94     char* bufp=(char*)buf;
 95     int nleft=maxline;
 96     while(1)
 97     {
 98         ret=recv_peek(sockfd,bufp,nleft);
 99         if(ret<0)
100             return ret;
101         else if(0==ret)
102             return ret;
103         nread=ret;
104         int i;
105         for(i=0;i<nread;i++)//读取到‘\n‘时就应该结束
106         {
107             if(bufp[i]==‘\n‘)
108             {
109                 ret=readn(sockfd,bufp,i+1);
110                 if(ret!=i+1)
111                     exit(EXIT_FAILURE);
112                 return ret;
113             }
114         }
115         //执行到了这儿,说明没有读取到‘\n‘
116         if(nread>nleft)
117             exit(EXIT_FAILURE);
118         nleft-=nread;
119         ret=readn(sockfd,bufp,nread);//清空缓存中的数据,因为之前是窥视,并没有清空缓存
120         if(ret!=nread)
121             exit(EXIT_FAILURE);
122         bufp+=nread;//指针后移,接着去执行while循环,确保数据追加到原来已读取数据的末尾
123     }
124     return -1;
125 }
126 int main(void)
127 {
128     int listenfd;
129     if((listenfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
130         ERR_EXIT("socket");
131
132     struct sockaddr_in servaddr;
133     memset(&servaddr,0,sizeof(servaddr));
134     servaddr.sin_family=AF_INET;
135     servaddr.sin_port=htons(5188);
136     servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
137     //套接字选项的设置一定要在bind之前
138     int on=1;
139     if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
140             ERR_EXIT("setsockopt");
141     if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
142         ERR_EXIT("bind");
143     if(listen(listenfd,SOMAXCONN)<0)
144         ERR_EXIT("listen");
145     struct sockaddr_in peeraddr;
146     socklen_t peerlen=sizeof(peeraddr);
147     int conn;
148     pid_t pid;
149     while(1)
150     {
151         if((conn=accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen))<0)
152             ERR_EXIT("accept");
153         printf("ip=%s port=%d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
154         pid=fork();
155         if(-1==pid)
156             ERR_EXIT("fork");
157         if(0==pid)//子进程
158         {
159             close(listenfd);
160             do_service(conn);
161             exit(EXIT_SUCCESS);
162         }
163         else close(conn);//父进程
164     }
165
166
167     return 0;
168 }
169
170
171
172
173         

这是客户端代码:

  1 #include<unistd.h>
  2 #include<sys/types.h>
  3 #include<sys/socket.h>
  4 #include<netinet/in.h>
  5 #include<arpa/inet.h>
  6 #include<stdlib.h>
  7 #include<stdio.h>
  8 #include<errno.h>
  9 #include<string.h>
 10
 11 #define ERR_EXIT(m)  12         do  13         {  14             perror(m);  15             exit(EXIT_FAILURE); 16         }while(0)
 17
 18 ssize_t readn(int fd,void *buf,size_t count)
 19 {
 20     size_t nleft=count;
 21     ssize_t nread;
 22     char*bufp=(char*)buf;
 23     while(nleft>0)
 24     {
 25         if((nread=read(fd,bufp,nleft))<0)
 26         {
 27             if(errno==EINTR)
 28                 continue;
 29             return -1;
 30         }
 31         else if(nread==0)
 32             return count-nleft;
 33         bufp+=nread;
 34         nleft-=nread;
 35     }
 36     return count;
 37 }
 38
 39 ssize_t writen(int fd,const void*buf,size_t count)
 40 {
 41     size_t nleft=count;
 42     ssize_t nwritten;
 43     char*bufp=(char*)buf;
 44     while(nleft>0)
 45     {
 46         if((nwritten=write(fd,bufp,nleft))<0)
 47         {
 48             if(errno==EINTR)
 49                 continue;
 50             return -1;
 51         }
 52         else if(nwritten==0)
 53             continue;
 54         bufp+=nwritten;
 55         nleft-=nwritten;
 56     }
 57     return count;
 58 }
 59
 60 ssize_t recv_peek(int sockfd,void *buf,size_t len)
 61 {
 62     while(1)
 63     {
 64         int ret=recv(sockfd,buf,len,MSG_PEEK);//窥看,不清除缓存中的数据
 65         if(-1==ret&&EINTR==errno)
 66             continue;
 67         return ret;
 68     }
 69 }
 70
 71 ssize_t readline(int sockfd,void* buf,size_t maxline)
 72 {
 73     int ret;
 74     int nread;
 75     char* bufp=(char*)buf;
 76     int nleft=maxline;
 77     while(1)
 78     {
 79         ret=recv_peek(sockfd,bufp,nleft);
 80         if(ret<0)
 81             return ret;
 82         else if(0==ret)
 83             return ret;
 84         nread=ret;
 85         int i;
 86         for(i=0;i<nread;i++)//读取到‘\n‘时就应该结束
 87         {
 88             if(bufp[i]==‘\n‘)
 89             {
 90                 ret=readn(sockfd,bufp,i+1);
 91                 if(ret!=i+1)
 92                     exit(EXIT_FAILURE);
 93                 return ret;
 94             }
 95         }
 96         //执行到了这儿,说明没有读取到‘\n‘
 97         if(nread>nleft)
 98             exit(EXIT_FAILURE);
 99         nleft-=nread;
100         ret=readn(sockfd,bufp,nread);//清空缓存中的数据,因为之前是窥视,并没有清空缓存
101         if(ret!=nread)
102             exit(EXIT_FAILURE);
103         bufp+=nread;//指针后移,接着去执行while循环,确保数据追加到原来已读取数据的末尾
104     }
105     return -1;
106 }
107 int main(void)
108 {
109     int sock;
110     if((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
111         ERR_EXIT("socket");
112     struct sockaddr_in servaddr;
113     memset(&servaddr,0,sizeof(servaddr));
114     servaddr.sin_family=AF_INET;
115     servaddr.sin_port=htons(5188);
116     servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");//inet_addr将ip地址字符串转为数字形式,且已经是网络字节序
117
118     if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)//
119         ERR_EXIT("connect");
120
121     char sendbuf[1024]={0};
122     char recvbuf[1024]={0};
123     while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL)
124     {
125         writen(sock,sendbuf,strlen(sendbuf));
126
127         int ret=readline(sock,recvbuf,sizeof(recvbuf));
128         if(-1==ret)
129             ERR_EXIT("readline");
130         else if(0==ret)
131         {
132             printf("client?server close\n");
133             break;
134         }
135         fputs(recvbuf,stdout);
136     }
137
138     return 0;
139 }
时间: 2024-10-12 13:12:43

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

TCP学习之建立一个简单的客户/服务器系统--回射系统

TCP学习之建立一个简单的客户/服务器系统--回射系统 相关的资料可以参考Unix网络编程,这个只是修改版,不需要依赖任何文件,可以独立编译通过,而且是在两台不同的主机上进行的. fggets和fputs这两个函数来自标准I/O函数库,writen和readline见my_unp.h头文件 //my_unp.h #include<stdarg.h> #include<syslog.h> #include<stdio.h> #include<netinet/in.h

服务器客户端回射程序-自己设计包的结构

这次是个点对点,不过我自己设计包,包中包括发送的字符串的长度,和实际的字符串,使用结构体来表示. 客户端跟服务器在接收报文时,首先接收字符串的长度这一数值,然后将这一数值作为参数传入readn接收固定长度的字节数字符串. 看代码,首先是服务器端: 1 /*使用发送固定字节数报文的点对点聊天程序*/ 2 #include<stdio.h> 3 #include<unistd.h> 4 #include<sys/types.h> 5 #include<sys/sock

封装一个运动函数

1 //支持 缓冲 + 多物体 + 链式 + 完美 2 //obj:运动的对象 3 //json:存储多个attr和target 4 //callback :回调函数 代表一个功能 当一个函数作为参数时,这样的函数叫做回调函数 5 function startMove(obj,json,callback){ 6 clearInterval( obj.timer );//在运动之前先清空定时器 7 obj.timer = setInterval( function(){ 8 var flag =

Socket 入门- 客户端回射程序

结果输出:------------------------------------------------------客户端:[email protected]:~/Public/C$ ./postBackCli.out 127.0.0.1connect OKaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcc#close OK[email protected]:~/Public/C$ ---------------

一个简单的JAVA服务器-客户端模型

package com.sxt.udp; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketException; public class UdpClient { public static void main(String[] args) throws Exception { System.out.pri

js 封装一个动画函数

//动画函数---任意一个元素移动到指定的目标位置 //element为元素 target为位置 function carToon(element, target) { //设置一个定时器让他循环去增加 element.timeid = setInterval(function () { //拿到当前的位置(纯数字) var current = element.offsetLeft; //每次要移动的像素current var step = 10; //注意 这里是判断到底往那边走 如果当前的位

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

传统的signal可能会有信号未决或者信号重入或多或少的问题,毕竟这个函数已经很多年了. 所以推荐使用sigaction函数,但是sigaction函数相对signal较为复杂,而且每次要写一大堆.因此对于习惯使用signal这种简单方便的函数我们不妨自己包一下sigaction! int MySigaction(int signo, void (*func)(int)) { struct sigaction act, oact; act.sa_handler = func; sigemptys

一个简单的JAVA服务器-客户端模型-tcp

package com.sxt.tcp; import java.io.DataInputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws IOException { System.out.println("----Server---

封装一个分页函数

<?php  /**   * 分页链接生成函数   * @param int $page 当前访问的页码   * @param int $total_page 总页数   * @return string 拼接好的url地址   */ function showPage($page,$total_page){  //拼接"首页"链接 $html = '<a href="?page=1">[首页]</a>';  //拼接"上一页