linux网络编程:使用单进程实现多客户端通信
服务端:
//回射服务器
//避免僵尸进程
#include "unistd.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "sys/wait.h"
#include "netinet/in.h"
#include "arpa/inet.h"
#include "stdlib.h"
#include "stdio.h"
#include "errno.h"
#include "string.h"
#include "signal.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; //剩余字节数 //在32位系统中size_t是4字节的,而在64位系统中,size_t是8字节的 增加可移植性
ssize_t nread;//读到的字节数 //ssize_t:这个数据类型用来表示可以被执行读写操作的数据块的大小,它表示的是sign size_t类型的。
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 == - && errno == EINTR)
continue;
return ret;
}
} ssize_t readline(int sockfd , void *buf , size_t maxline)
{
int ret;
int nread;
char *bufp = 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++)
{
if (bufp[i] == '\n')
{
ret = readn(sockfd,bufp,i+); // i+1 包括 \n
if (ret != i + )
exit(EXIT_FAILURE);
return ret;
}
}
if (nread > nleft)
exit(EXIT_FAILURE);
nleft -= nread;
ret = readn(sockfd , bufp , nread);
if (ret != nread)
exit(EXIT_FAILURE);
bufp += nread;
}
return -; //运行到这里就出错了
} void echo_srv(int conn)
{
char recvbuf[];
int n;
while()
{
memset(&recvbuf,,sizeof(recvbuf));
int ret = readline(conn,recvbuf,);
//扑捉客户端关闭
if (ret == -)
ERR_EXIT("readline");
if (ret == )
{
printf("clientclose\n");
break;
}
fputs(recvbuf,stdout);
writen(conn,recvbuf,strlen(recvbuf));
}
} void handle_sigchld(int sig)
{
// wait(NULL);
while (waitpid(-,NULL,WNOHANG) > );//waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。
} int main(int argc, char const *argv[])
{
// signal(SIGCHLD,SIG_IGN);//避免僵尸进程
signal(SIGCHLD,handle_sigchld);//避免僵尸进程
int listenfd;
if ((listenfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < ) //IPv4为PF_INET,IPv6为PF_INET6;TCP传输时为SOCK_STREAM, UDP位SOCK_DGRAM
ERR_EXIT("socket");
struct sockaddr_in servaddr;
memset(&servaddr,,sizeof(servaddr));
servaddr.sin_family = AF_INET; //设置地址家族 协议族,在socket编程中只能是AF_INET
servaddr.sin_port = htons(); //设置端口
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //设置地址
// servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
// inet_aton("127.0.0.1",&servaddr.sin_addr); int on = ;
if (setsockopt(listenfd, SOL_SOCKET , SO_REUSEADDR, &on , sizeof(on)) < )//setsockopt()函数,用于任意类型、任意状态套接口的设置选项值。
ERR_EXIT("setsockopt"); if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < )
ERR_EXIT("bind");
/*定义函数:int bind(int sockfd, struct sockaddr * my_addr, int addrlen);
函数说明:bind()用来设置给参数sockfd 的socket 一个名称. 此名称由参数my_addr 指向一sockaddr 结构,对于不同的socket domain 定义了一个通用的数据结构
*/
if (listen(listenfd,SOMAXCONN) < )
ERR_EXIT("listen"); struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int conn; // pid_t pid;
// while (1)
// {
// if ((conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen)) < 0)
// ERR_EXIT("accept");
// /*
// 定义函数:int accept(int s, struct sockaddr * addr, int * addrlen);
// 函数说明:accept()用来接受参数s 的socket 连线. 参数s 的socket 必需先经bind()、listen()函数处理过,
// 当有连线进来时accept()会返回一个新的socket 处理代码, 往后的数据传送与读取就是经由新的socket处理,
// 而原来参数s 的socket 能继续使用accept()来接受新的连线要求. 连线成功时, 参数addr
// 所指的结构会被系统填入远程主机的地址数据, 参数addrlen 为scokaddr 的结构长度. 关于机构sockaddr 的定义请参考bind().
// */
// printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr) , ntohs(peeraddr.sin_port));
// //inet_ntoa 将网络地址转换成“.”点隔的字符串格式。
// //ntohs 本函数将一个16位数由网络字节顺序转换为主机字节顺序。
//
// pid = fork();
// if (pid == -1)
// ERR_EXIT("fork");
// if (pid == 0)
// {
// close(listenfd);
// echo_srv(conn);
// exit(EXIT_SUCCESS);
// }else
// close(conn);
// } int client[FD_SETSIZE];//保存客户端文件描述符
int maxi = ;
int i;
for(i= ; i < FD_SETSIZE;i++)
client[i] = -; int nready;
int maxfd = listenfd;
fd_set rset;
fd_set allset;
FD_ZERO(&rset);
FD_ZERO(&allset);
FD_SET(listenfd,&allset);
while ()
{
rset = allset;
nready = select(maxfd+,&rset,NULL,NULL,NULL);
if (nready == -)
{
if (errno == EINTR)
continue;
ERR_EXIT("select");
}
if (nready == )
continue;
if (FD_ISSET(listenfd,&rset))
{
peerlen = sizeof(peeraddr);
conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen);
if (conn == -)
ERR_EXIT("accept");
for (i=;i<FD_SETSIZE;i++)
{
if (client[i] < )
{
client[i] = conn;
if (i > maxi)
maxi = i;
break;
}
}
if (i == FD_SETSIZE)
{
fprintf(stderr,"too many clients\n");
exit(EXIT_FAILURE);
}
printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr) , ntohs(peeraddr.sin_port));
FD_SET(conn,&allset);
if (conn > maxfd)
maxfd = conn; if (--nready <= )
continue;
} for(i=;i<=maxi;i++)
{
conn = client[i];
if (conn == -)
continue;
if (FD_ISSET(conn,&rset))
{
char recvbuf[] = {};
int ret = readline(conn,recvbuf,);
//扑捉客户端关闭
if (ret == -)
ERR_EXIT("readline");
if (ret == )
{
printf("clientclose\n");
FD_CLR(conn,&allset);
client[i] = -;
}
fputs(recvbuf,stdout);
writen(conn,recvbuf,strlen(recvbuf)); if (--nready <= )
break;
}
}
}
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"
#include "signal.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 == - && errno == EINTR)
continue;
return ret;
}
} ssize_t readline(int sockfd , void *buf , size_t maxline)
{
int ret;
int nread;
char *bufp = 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++)
{
if (bufp[i] == '\n')
{
ret = readn(sockfd,bufp,i+); // i+1 包括 \n
if (ret != i + )
exit(EXIT_FAILURE);
return ret;
}
}
if (nread > nleft)
exit(EXIT_FAILURE);
nleft -= nread;
ret = readn(sockfd , bufp , nread);
if (ret != nread)
exit(EXIT_FAILURE);
bufp += nread;
}
return -; //运行到这里就出错了
} void echo_cli(int sock)
{
// char sendbuf[1024] = {0};
// char recvbuf[1024] = {0};
// while (fgets(sendbuf,sizeof(sendbuf),stdin) != NULL)
// {
// writen(sock,sendbuf,strlen(sendbuf)); //包头加包体
// int ret = readline(sock ,recvbuf,sizeof(recvbuf));
// //扑捉客户端关闭
// if (ret == -1)
// ERR_EXIT("readline");
// else if (ret == 0)
// {
// printf("client close\n");
// break;
// }
// fputs(recvbuf,stdout);
// memset(sendbuf , 0 , sizeof(sendbuf));
// memset(recvbuf , 0 , sizeof(recvbuf));
// }
// close(sock);
fd_set rset;
FD_ZERO(&rset); int nready;
int maxfd;
int fd_stdin = fileno(stdin);
if (fd_stdin > sock)
maxfd = fd_stdin;
else
maxfd = sock; char sendbuf[] = {};
char recvbuf[] = {};
while ()
{
FD_SET(fd_stdin,&rset);
FD_SET(sock,&rset);
nready = select(maxfd+,&rset,NULL,NULL,NULL);
if (nready == -)
ERR_EXIT("select");
if (nready == )
continue;
if (FD_ISSET(sock,&rset))
{
int ret = readline(sock ,recvbuf,sizeof(recvbuf));
//扑捉客户端关闭
if (ret == -)
ERR_EXIT("readline");
else if (ret == )
{
printf("server close\n");
break;
}
fputs(recvbuf,stdout);
memset(recvbuf , , sizeof(recvbuf));
} if (FD_ISSET(fd_stdin,&rset))
{
if (fgets(sendbuf,sizeof(sendbuf),stdin) == NULL)
break;
writen(sock,sendbuf,strlen(sendbuf));
memset(sendbuf,,sizeof(sendbuf));
}
}
close(sock); } void handle_sigpipe(int sig)
{
printf("recv a sig=%d\n",sig);
} int main(void)
{
signal(SIGPIPE,handle_sigpipe);
int sock;
if ((sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < ) //IPv4为PF_INET,IPv6为PF_INET6;TCP传输时为SOCK_STREAM, UDP位SOCK_DGRAM
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"); //设置地址 if (connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr)) < )
ERR_EXIT("connect"); struct sockaddr_in localaddr;
socklen_t addrlen = sizeof(localaddr);
if (getsockname(sock , (struct sockaddr*)&localaddr , &addrlen) < )
ERR_EXIT("getsockname");
printf("ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr) , ntohs(localaddr.sin_port));
echo_cli(sock);
return ;
}
linux网络编程:使用单进程实现多客户端通信的更多相关文章
- java网络编程-单线程服务端与客户端通信
该服务器一次只能处理一个客户端请求;p/** * 利用Socket进行简单服务端与客户端连接 * 这是服务端 */public class EchoServer { private ServerSoc ...
- Linux网络编程(六)
网络编程中,使用多路IO复用的典型场合: 1.当客户处理多个描述字时(交互式输入以及网络接口),必须使用IO复用. 2.一个客户同时处理多个套接口. 3.一个tcp服务程序既要处理监听套接口,又要处理 ...
- Linux网络编程(五)
/*Linux网络编程(五)——多路IO复用之select() 网络编程中,使用IO复用的典型场合: 1.当客户处理多个描述字时(交互式输入以及网络接口),必须使用IO复用. 2.一个客户同时处理多个 ...
- 【linux草鞋应用编程系列】_5_ Linux网络编程
一.网络通信简介 第一部分内容,暂时没法描述,内容实在太多,待后续专门的系列文章. 二.linux网络通信 在linux中继承了Unix下“一切皆文件”的思想, 在linux中要实现网 ...
- linux网络编程-(socket套接字编程UDP传输)
今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...
- linux网络编程_1
本文属于转载,稍有改动,以利于学习. (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个 ...
- Linux网络编程入门 (转载)
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- [转] - Linux网络编程 -- 网络知识介绍
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- Linux C 程序 Linux网络编程(21)
Linux网络编程网络编程必备的理论基础网络模型,地址,端口,TCP/IP协议 TCP/IP协议是目前世界上使用最广泛的网络通信协议日常中的大部分应用使用该系列协议(浏览网页,收发电子邮件,QQ聊天等 ...
- 【转】Linux网络编程入门
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
随机推荐
- shell中的数学运算
shell中要进行数学运算通常有3中方法: expr命令 比如 expr 1 + 6就会返回7,使用expr的缺点就是碰到乘法运算,或者加括号(因为它们在shell中有其他意义),需要使用转义,比如: ...
- 极客”一词,来自于美国俚语“geek”的音译,一般理解为性格古怪的人
起源 “ 极客”一词,来自于美国俚语“ geek”的音译,一般理解为性格古怪的人.数学“极客”大多是指,并不 一定是数学专业但又对数学等技术有狂热的兴趣并投入大量时间钻研的人.又 译作“ 奇客”.以前 ...
- java学会需要掌握的知识(来源网上。。)
Java就业指导 2016-03-22 骆昊 程序人生 点击上方"程序人生"关注我们 想要成为合格的Java程序员或工程师到底需要具备哪些专业技能,面试者在面试之前到底需要准备哪些 ...
- Fedora19/18/17安装显卡驱动和无限网卡驱动
一.安装nvidia显卡驱动 1. 切换到root用户 su - 2. 确定当前Linux内核及SELinux policy 是否为最新 yum update ke ...
- 如何在jQuery中使用 setInterval,setTimeout
当遇到setInterval,setTimeout与jquery混用的问题 时,直接按JavaScript中的语法写并不起作用,有以下两种解决方法. 方法1. 直接在ready中调用其他方法,会提示缺 ...
- Windows安装TensorFlow-Docker Installation of TensorFlow on Windows
TensorFlow是Google开发的进行Deep Learning的包,目前只是支持在Linux和OSX上运行.不过这个秋季或许就有支持Windows的版本出现了,那么对于使用Windows的开发 ...
- freebsd安装和图形界面安装
通过上述的安装以后只有命令行界面,没有图形界面,如果想使用X Window就需要安装X 11和GNOME(或KDE). 1. 安装X 11(用root账户) 在确保虚拟机的CD-ROM加载了FreeB ...
- NFC应用(一)卡应用
门禁卡.停车卡.公交卡工作于NFC的卡模式,是目前日常生活中接触得最多的NFC应用场合.一张小小的卡片,轻触读卡器使可开门禁锁.进出停车场.支付车资,即快捷方便,又安全,易于管理. 以门禁系统为例,通 ...
- windows bat脚本编写
windows批处理 (cmd/bat) 编程详解 开始之前先简单说明下cmd文件和bat文件的区别:在本质上两者没有区别,都是简单的文本编码方式,都可以用记事本创建.编辑和查看.两者所用的命令行代码 ...
- 2.x ESL第二章习题 2.8
题目 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 3 ...