SOCKET简单框架

本文详细介绍了服务器端和客户端通信技术的实现方法,包括使用 select、POLL 和 EPOLL 的不同方式来优化网络通信效率。从底层原理到具体代码实现,涵盖了从创建套接字、监听连接、读写数据到关闭连接的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用select

服务端


#include <stdio.h>

#include <sys/socket.h>

#include <netdb.h>
#include <sys/select.h>
#include <list>
#include <iostream>
#include <string.h>
#include<arpa/inet.h>


#define gPort 6000

using namespace std;

list<int> ClientSock;

void setClientFd(fd_set *rd)
{
	for(list<int>::iterator i=ClientSock.begin(); i!=ClientSock.end(); ++i) {
		FD_SET(*i, rd);
	}
}



int main()
{

	int sock, newsock, addrlen, MaxFd;

	int sl;

	struct sockaddr_in SerAddr, ClientAddr;

	char Buf[1024];

	fd_set rd, wd;
	int val;
	

	sock = socket(AF_INET, SOCK_STREAM, 0);

	

	SerAddr.sin_family = AF_INET;

	SerAddr.sin_port = htos(gPort);

	SerAddr.sin_addr.s_addr = htonl(INADDR_ANY);

	val = 1;
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(val));
	

	bind(sock, (struct sockaddr *)&SerAddr, sizeof(SerAddr));

	

	listen(sock, 5);

	

	addrlen = sizeof(struct sockaddr_in);

	
	MaxFd = sock+1;
	

	while(1)

	{
		FD_ZERO(&rd);
		FD_SET(sock, &rd);
		setClientFd(&rd);
		
		sl = select(MaxFd, &rd, NULL, NULL, NULL);
		
		if(sl>0) {
			if(FD_ISSET(sock, &rd)) {
				newsock= accept(sock, (struct sockaddr *)&ClientAddr, (socklen_t*)&addrlen);
				if(newsock >= MaxFd) {
					MaxFd = newsock+1;
				}
				cout<<"accept one connecton"<<endl;

				cout<<"addr:"<< inet_ntoa(ClientAddr.sin_addr) <<"port:"<<  ntohs(ClientAddr.sin_port) << endl;
				ClientSock.push_back(newsock);
			}
			for(list<int>::iterator i=ClientSock.begin(); i!=ClientSock.end();) {
				if(FD_ISSET(*i, &rd)) {
					memset(Buf, 0, sizeof(Buf));

					sl = recv(*i, Buf, sizeof(Buf), 0);

					cout<<"recv: "<< Buf <<endl;
					send(*i, "recv done", 9, 0);
					if(memcmp(Buf, "close", 5) == 0) {

						close(*i);
						i = ClientSock.erase(i);
					} else {
						++i;
					}
				} else {
					++i;
				}
			}
		}

		

	}


	close(sock);

	

	return 0;

}

客户端

#include <stdio.h>

#include <sys/socket.h>

//#include <sys/types.h>
//#include <netinet/in.h>
#include <netdb.h>
#include <string.h>


#define gSerAddr "127.0.0.1"

#define gSerPort 6000


#define STDIN 0

int main()
{

	int sock, sl, MaxFd;

	struct sockaddr_in SerAddr;

	char Buf[1024];

	fd_set rd, wd;
	

	sock = socket(AF_INET, SOCK_STREAM, 0);

	

	SerAddr.sin_family = AF_INET;

	SerAddr.sin_port = htons(gSerPort);

	SerAddr.sin_addr.s_addr = inet_addr(gSerAddr);

	

	sl = connect(sock, &SerAddr, sizeof(SerAddr));

	
	if(sl<0) {
		perror("connect err\r\n");
		return -1;
	}
	
	MaxFd = (sock>STDIN ? sock+1 : STDIN+1);
	
	while(1) {
		FD_ZERO(&rd);
		//FD_ZERO(&wd);
		FD_SET(sock, &rd);
		FD_SET(STDIN, &rd);
	
		sl = select(MaxFd, &rd, NULL, NULL, NULL);
		if(sl>0) {
			if(FD_ISSET(sock,&rd)) {
				memset(Buf, 0, sizeof(Buf));

				sl = recv(sock, Buf, sizeof(Buf), 0);

				printf("recv:%d %s \n", sl, Buf);
			}
			if(FD_ISSET(STDIN, &rd)) {
				sl = read(STDIN, Buf, sizeof(Buf));
				send(sock, Buf, sl, 0);
				if(memcmp(Buf, "close", 5)==0) {
					close(sock);
					break;
				}
			}
		}



	}

	

	return 0;

}

使用POLL

服务端

#include <stdio.h>

#include <sys/socket.h>

#include <netdb.h>
#include <sys/select.h>
#include <list>
#include <iostream>
#include <string.h>
#include <arpa/inet.h>
#include <poll.h>
#include <vector>


#define gPort 6000

using namespace std;




int main()

{

	int sock, newsock, addrlen;

	int sl;

	struct sockaddr_in SerAddr, ClientAddr;

	char Buf[1024];

	vector<struct pollfd> pfd;
	struct pollfd nfd;
	int val;
	
	

	sock = socket(AF_INET, SOCK_STREAM, 0);

	

	SerAddr.sin_family = AF_INET;

	SerAddr.sin_port = htons(gPort);

	SerAddr.sin_addr.s_addr = htonl(INADDR_ANY);

	val = 1;
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(val));
	

	bind(sock, (struct sockaddr *)&SerAddr, sizeof(SerAddr));

	

	listen(sock, 5);

	

	addrlen = sizeof(struct sockaddr_in);
	nfd.fd = sock;
	nfd.events = POLLIN | POLLPRI;
	pfd.push_back(nfd);
	

	while(1)

	{
		sl = poll(&pfd[0], pfd.size(), -1);
		
		if(sl>0) {
			if((pfd[0].revents & POLLIN) || (pfd[0].revents & POLLPRI)) {
				newsock= accept(sock, (struct sockaddr *)&ClientAddr, (socklen_t*)&addrlen);
				cout<<"accept one connecton"<<endl;

				cout<<"addr:"<< inet_ntoa(ClientAddr.sin_addr) <<"port:"<<  ntohs(ClientAddr.sin_port) << endl;
				nfd.fd = newsock;
				nfd.events = POLLIN | POLLPRI;
				pfd.push_back(nfd);
			}
			for(vector<struct pollfd>::iterator i=pfd.begin()+1; i!=pfd.end();) {
				if((i->revents & POLLIN) || (i->revents & POLLPRI)) {
					memset(Buf, 0, sizeof(Buf));

					sl = recv(i->fd, Buf, sizeof(Buf), 0);

					cout<<"recv: "<< Buf <<endl;
					send(i->fd, "recv done", 9, 0);
					if(memcmp(Buf, "close", 5) == 0) {

						close(i->fd);
						i = pfd.erase(i);
					} else {
						++i;
					}
				} else {
					++i;
				}
			}
		} else {
			cout<<"poll wrong"<<endl;
		}

		

	}


	close(sock);

	

	return 0;

}


客户端

#include <stdio.h>

#include <sys/socket.h>

//#include <sys/types.h>
//#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <poll.h>
#include <unistd.h>


#define gSerAddr "127.0.0.1"

#define gSerPort 6000





int main()

{

	int sock, sl;

	struct sockaddr_in SerAddr;

	char Buf[1024];

	struct pollfd pfd[2];
	

	sock = socket(AF_INET, SOCK_STREAM, 0);

	

	SerAddr.sin_family = AF_INET;

	SerAddr.sin_port = htons(gSerPort);

	SerAddr.sin_addr.s_addr = inet_addr(gSerAddr);

	

	sl = connect(sock, &SerAddr, sizeof(SerAddr));

	
	if(sl<0) {
		perror("connect err\r\n");
		return -1;
	}
	
	pfd[0].fd = sock;
	pfd[0].events = POLLIN | POLLPRI;
	pfd[1].fd = STDIN_FILENO;
	pfd[1].events = POLLIN| POLLPRI;
	
	while(1) {
		sl = poll(pfd, 2, -1);
		if(sl>0) {
			if((pfd[0].events & POLLIN) || (pfd[0].events & POLLPRI) ) {
				memset(Buf, 0, sizeof(Buf));

				sl = recv(sock, Buf, sizeof(Buf), 0);

				printf("recv:%d %s \n", sl, Buf);
			}
			if((pfd[1].events & POLLIN) || (pfd[1].events & POLLPRI) ) {
				sl = read(STDIN_FILENO, Buf, sizeof(Buf));
				send(sock, Buf, sl, 0);
				if(memcmp(Buf, "close", 5)==0) {
					close(sock);
					break;
				}
			}
		}



	}

	

	return 0;

}

使用EPOLL

服务端

#include <stdio.h>

#include <sys/socket.h>

#include <netdb.h>
#include <sys/select.h>
#include <list>
#include <iostream>
#include <string.h>
#include <arpa/inet.h>
#include <sys/epoll.h>


#define gPort 6000

using namespace std;

const int EvtNum = 100;


int main()

{

	int sock, newsock, addrlen;

	int sl, ret;

	struct sockaddr_in SerAddr, ClientAddr;

	char Buf[1024];
	int efd;
	int val;
	struct epoll_event evt;
	struct epoll_event QueryEvt[EvtNum];
	

	sock = socket(AF_INET, SOCK_STREAM, 0);

	

	SerAddr.sin_family = AF_INET;

	SerAddr.sin_port = htons(gPort);

	SerAddr.sin_addr.s_addr = htonl(INADDR_ANY);

	val = 1;
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(val));
	

	bind(sock, (struct sockaddr *)&SerAddr, sizeof(SerAddr));

	

	listen(sock, 5);

	

	addrlen = sizeof(struct sockaddr_in);
	
	efd = epoll_create(10);
	evt.events = EPOLLIN | EPOLLPRI;
	evt.data.fd = sock;
	epoll_ctl(efd, EPOLL_CTL_ADD, sock, &evt);
	

	while(1)

	{
		sl = epoll_wait(efd, QueryEvt, EvtNum, 1000);
		
		if(sl>0) {
			ret = sl;
			for(int i=0; i<ret; ++i) {
				if((QueryEvt[i].events & EPOLLIN) || (QueryEvt[i].events & EPOLLPRI)) {
					if(QueryEvt[i].data.fd == sock) {
						newsock= accept(sock, (struct sockaddr *)&ClientAddr, (socklen_t*)&addrlen);
						cout<<"accept one connecton"<<endl;

						cout<<"addr:"<< inet_ntoa(ClientAddr.sin_addr) <<"port:"<<  ntohs(ClientAddr.sin_port) << endl;
						evt.data.fd = newsock;
						evt.events = EPOLLIN | EPOLLPRI;
						epoll_ctl(efd, EPOLL_CTL_ADD, newsock, &evt);
					} else {
						memset(Buf, 0, sizeof(Buf));

						sl = recv(QueryEvt[i].data.fd, Buf, sizeof(Buf), 0); 
						if(sl<=0){

							close(QueryEvt[i].data.fd);
							epoll_ctl(efd, EPOLL_CTL_DEL, QueryEvt[i].data.fd, 0);
							continue;
						}
						cout<<"recv: "<< Buf <<endl;
						send(QueryEvt[i].data.fd, "recv done", 9, 0);
						if(memcmp(Buf, "close", 5) == 0) {

							close(QueryEvt[i].data.fd);
							epoll_ctl(efd, EPOLL_CTL_DEL, QueryEvt[i].data.fd, 0);					
						} 
					}
				}
			}
		} else if(sl==0) {
			continue; 
		}else {
			cout<<"epoll wrong"<<endl;
		}

		

	}


	close(sock);

	

	return 0;

}



客户端

#include <stdio.h>

#include <sys/socket.h>

//#include <sys/types.h>
//#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>

#include <fcntl.h>
#include <sys/epoll.h>

#define gSerAddr "127.0.0.1"

#define gSerPort 6000





int main()

{

	int sock, sl, i;

	struct sockaddr_in SerAddr;

	char Buf[1024];
	struct epoll_event QryEt[2];
	

	sock = socket(AF_INET, SOCK_STREAM, 0);

	

	SerAddr.sin_family = AF_INET;

	SerAddr.sin_port = htons(gSerPort);

	SerAddr.sin_addr.s_addr = inet_addr(gSerAddr);

	

	sl = connect(sock, &SerAddr, sizeof(SerAddr));

	
	if(sl<0) {
		perror("connect err\r\n");
		return -1;
	}
	
	int efd = epoll_create(2);
	struct epoll_event ev;
	ev.events = EPOLLIN | EPOLLPRI;
	ev.data.fd = sock;
	epoll_ctl(efd, EPOLL_CTL_ADD, sock, &ev);
	ev.data.fd = STDIN_FILENO;
	epoll_ctl(efd, EPOLL_CTL_ADD, STDIN_FILENO, &ev);
	
	while(1) {
		sl = epoll_wait(efd, QryEt, 2, 1000);
		if(sl>0) {
			for(i=0; i<sl; ++i)
				if((QryEt[i].events & EPOLLIN) || (QryEt[i].events & EPOLLPRI) ) {
					if(QryEt[i].data.fd == sock) {
						memset(Buf, 0, sizeof(Buf));

						sl = recv(sock, Buf, sizeof(Buf), 0);

						printf("recv:%d %s \n", sl, Buf);	
					} else if(QryEt[i].data.fd == STDIN_FILENO) {
						sl = read(0, Buf, sizeof(Buf));
						sl = send(sock, Buf, sl, 0);
						printf("send %d \n", sl);
						if(memcmp(Buf, "close", 5)==0) {
							close(sock);
							break;
						}
					}
				}
		} else if (sl==0) {
			continue;
		} else {
			printf("epoll_wait wrong \n");
		}

	}
	

	return 0;

}



感谢大家对北风之神SOCKET框架的支持。鼓励。下面是北风之神 3.1的更新内容: 修正BUG: 1.ZYSocketSuper 读取 配置文件的最大连接数 读错问题。 2.ZYSocketSuper 无法断开客户端的问题。 3.BuffList 数据包解析丢失问题。 4.例1,例2.客户端断开忘记释放调用SOCKET.CLOSE()的问题 新增功能 1.添加了一个ReadBytes 构造函数,此函数实现了在数据包在读取前需要回调的方法传入。(可以用来解密,解压缩 等功能) 2.添加了一个BufferFormat 类的构造,此函数实现了在数据包在生成前需要回调的方法传入。(可以用来加密,压缩 等功能) 3.添加了BufferFormat.FormatFCA(object o,FDataExtraHandle dataExtra)静态方法。可以用来在类格式化成数据包的时候进行加密压缩等功能 4.添加了ZYSocket.Security 命名空间,里面有传统的AES,DES算法的加解密类 5.添加了ZYSocket.Compression命名空间,里面有通过Deflate算法压缩类 6.开发了ReadBytes.Data属性,为ReadBytes里面的BYTE[]对象。值得注意的是 ReadBytes.Length为数据包原始长度,如果要得到解压缩后的数据包长度,请访问ReadByte.Data.length 新增代码 加解密实例测试 项目:演示了 AES DES 以及Deflate 的使用方法。 例3 - 例2的加密版 项目:就是讲例2通过DES 加密进行通讯的例子 连接测试工具 项目:很多朋友问我要连接数量测试工具。我一起的真的丢了。找不到了。所以重新写了一个 例4 项目:好多人让我写一个发送文件的例子,现在能如愿以偿了 by luyikk@126.com BLOG:http://blog.youkuaiyun.com/luyikk QQ:547386448
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值