# 头文件

Windows下 #include < WinSock2.h > #pragma comment(lib,"ws2_32")

WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
     std::cerr << "WSAStartup failed" << std::endl;
     return 1;
}

Linux下 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> //sockaddr_in #include <arpa/inet.h> //inet_pton #include <unistd.h> // close()关闭socket

# 创建套接字

int soecket(int _domain, int _type, int _protocol)

  • _domain 协议族 AF_INET IPV4 AF_INET6 IPV6 AF_LOCAL 本地通讯
  • _type 数据类型 SOCK_RAW 原始套接字 SOCK_STREAM TCP/IP SOCK_DGRAM UDP/IP
  • _protocol 协议类型 填0就好了,默认选择_type对应的协议
int fd = socket(AF_INET,SOCK_STERAM,0);
if(fd < 0){
   perror("socket");
}

# bind绑定 or connect连接

int bind(int _socket, const sockaddr* _addr, socklen_t _len) int connect(int _socket, const sockaddr* _addr, socklen_t _len)

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
// Server
addr.sin_addr.s_addr = INADDR_ANY;
// Windows
addr.sin_addr.S_un.S_addr = inet_addr("ip");
// Linux
inet_pton(AF_INET,"IP",&addr.sin_addr.s_addr);// arpa/inet.h
/* ------ */
bind(fd,(struct sockaddr*)&addr,sizeof(addr));
connect(fd,(struct sockaddr*)&addr,sizeof(addr));

# listen

int listen (int _socket, int _n)

  • _socket使用哪个socket监听
  • _n 队列长度
listen(fd,10); //只有服务端需要进行监听

# accept

int accept(int _socket, struct sockaddr* client_addr, socklen_t *addrlen) _socket 使用哪个套接字 client_addr接受到的客户端信息 *addrlen sizeof(struct sockaddr)

struct sockaddr_in clientAddr;
sockLen_t len = sizeof(clientAddr);
int clientfd = accept(fd,(struct sockaddr*)&clientAddr,&len);
// 如果不需要客服端的信息的话
int clientfd = accept(fd,NULL,NULL);

# 发送数据

# send

ssize_t send(int _socket, const void* buff, size_t _n,int flag)

const char * msg = "Hello World";
send(fd,msg,sizeof(msg),0);

# sendto

sendto(
	_in int fd, 
	_in	const char far* buf, // 数据缓冲区
	_in int len, // 缓冲区大小
	_in int flags, // 一般填0
	_in cosnt struct sockaddr far* to, // 发送的地址信息
	_in int toLen // sizeof(sockaddr)
)

# recv接收数据

ssize_t recv(int _socket, void *buff, size_t _n, int flag) 返回值: 0=连接关闭 >0 接收到的数据包大小 <0错误

char buf[1024];
recv(fd,buf,sizeof(buf),0);

# 关闭连接

int close(int _socket) // Linux int closesocket(int _socket) // Windows

# 端口复用

允许多个套接字同时监听同一个端口

int opt = 1;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

# I/O多路复用模型,及其函数

什么是I/O多路复用?当一个程序有多个I/O任务时,很有可能有部分任务需要去等待其他的操作,这时候一个一个等待,这样的串行编程太费时间了,可以将所有的文件描述符交给内核,让内核帮助程序管理,如果一个任务已经准备就绪,就通知程序某个文件描述符可以执行I/O任务了

# select函数

# 简介

这是一个跨平台的函数可以在Linux和Windows中使用,解决了服务端与多个客户端连接以及通> 讯问题,select是一个线性表,会从左边开始向右端逐个检测

# fd_set

用来定义一个集合,集合中存放所有需要管理的socket句柄

typedef struct fd_set {
	u_int fd_count;
	SOCKET fd_array[FD_SETSIZE];
} fd_set;

# 集合操作

FD_ZEOR(fd_set*); //初始化,清空
FD_SET(int fd,fd_set*) //添加
FD_CLR(int fd,fd_set*) //删除
FD_ISSET(int fd,fd_set*) //查询

# 函数简介

传入一个用FD_SET提前设置好的fd_set集合,select会检查fd_set中的每个socket,如果某个socket已经准备好了,那select会将它在fd_set的集合位置为1,如果没有准备好的集合位置为0 例如,如果是读集合,那fd_set集合位为1,说明该socket已经有得读了

#include <sys/select.h>
int select(
	__in int maxfd,
	__inout fd_set* readSet,
	__inout fd_set* writeSet,
	__inout fd_set* errorSet,
	__int const struct timeval* timeout
);

# 时间结构体

struct timeval{
     time_t tv_sec; //seconds
     suseconds_t tv_usec; //microseconds
}

# 返回值

return value:

大于 0 The num of fd is readied 等于 0 time out 小于 0 error

# 大致实例

在该文件夹中select.cpp有完成实例

fd_set read_fd_set
FD_ZERO(&read_fd_set);
FD_SET(fd,&read_fd_set);// 假设fd为客户端句柄
timeval tv = {1,0} // 设置超时时间
int res = select(1024,&read_fd_set,NULL,NULL&tv);
if(res == 0 || res == SOCKET_ERROR){ return 0; }
if(FD_ISSET(fd,&read_fd_set)){
	// 有得读了
}

# poll函数(只能在Linux系统中使用)

int poll(struct pollfd * fds, unsigned long nfds, int timeout) struct pollfd{ int fd;//套接字句柄 short events;//希望的事件 short revents;//实际的事件 } fds 传入pollfd数组 nfds 传入的数组大小 timeout INFTIM=无限等待,0=立刻返回,数值=超时时间

# epoll函数

此函数只能在Linux中使用 #include <sys/epoll.h>

# epoll_create

/*to create epoll fd*/
int epoll_create(int size); // size greater than 0
// return a epoll fd

# epoll_ctl

/*to contral fd from epoll*/
int epoll_ctl(int epollfd, int option, int fd, struct epoll_event* event);
/* options: 
EPOLL_CTL_ADD // add fd to epoll
EPOLL_CTL_MOD // modify fd from epoll
EPOLL_CTL_DEL // delete fd from epoll
*/

# epoll_event

struct epoll_event {
     uint32_t events;
     epoll_data_t data;   
}
/* events
EPOLLIN // 可读
EPOLLOUT // 可写
*/

# epoll_data

typedef struct epoll_data {
     void * ptr;
     int fd; // 一般只是用这个,用来保存这个事件是属于那个fd的
     uint32_t u32;
     uint64_t u64;    
}epoll_data_t;

# epoll_wait

/*to wait epoll_event*/
int epoll_wait(
	int epollfd, // epoll文件描述符
	struct epoll_event *eventBack, // 一个数组用来作为回调的数据
	int max_eventBack, // 数组大小
	int timeout
);
// return the num of event
/*timeout options:
= 0 noblock
> 0 wait n ms
-1 block
*/