流程:
1.创建listenfd 套接字 socket(AF_INET,SOCK_STREAM,0);
SOCK_STREAM 流的形式 TCP
2.创建地址族
Struct sockaddr_in servaddr;
Servaddr.sin_family =AN_INET; //IPV4
Servaddr.sin_addr.s_addr = htol(INADDR_ANY); // 0.0.0.0
Server.sin_port = htons(9999) // 端口
3.绑定端口
Bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
绑定 监听 地址族 地址族长度
4.创建一个客户端
Struct sockaddr_in clientfd;
Socklen_t len = sizeof(client);
5.接收
Int clientfd = accept(client,(struct sockaddr *)&client,&len);
Listen accept
Recv send
为什么是单次的,因为默认是阻塞的FD,没有数据不返回,一直在等
设置accept非阻塞:
Int flag = fcntl(listenfd,F_GETFL, 0 ); //获取当前模式
Flag |= O_NONBLOCK;
Fcntl(listenfd,F_SETFL,flag);
设置accept非阻塞后,结果值直接-1,并且永远返回不到。
因为非阻塞,直接返回了,还没有连接就返回了,除非用循环一直获取
发送数据,有时候可以收到,有时候收不到
结论:send返回正数,不等于发送成功
Send只是吧数据放入缓存,并没有发送出去
问题
1.一个客户端连接并且能够发送,另一个客户端能建立连接但是不能发送
2.当断开连接时,会循环recv,并且返回值为0
3.只能连接1次,不能再次连接
1的解法
原因:因为没有执行accept,每个客户端都要accept 但是建立连接不需要accept
解法:1.多线程或者多进程
一请求一线程,或者一请求一进程
一个线程只为一个fd服务
优点:逻辑简单
缺点:消耗内存
2.IO多路复用
2的解法
Recv返回0代表客户端断开(客户端调用close),
查看TCP 端口网络 netstat -anop|grep 端口号
所以 ,我们自己关闭close即可
Close的库是<unistd.h>
思考:单线程的情况下,多个客户端连接没有问题
1.无法进行accept
IO复用 检测IO是否有事件
select 管理所有IO
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#define BUFFER_LENGTH 128
// listenfd clientfd
struct sock_item{ // 用户 处理函数
int fd; // clientfd
char *rbuffer;// 数组处理超大字符串
int rlength;
char *wbuffer;
int wlength;
int event; // 读事件还是写事件
void (*recv_cb)(int fd,char *buffer,int lenght);
void (*send_cb)(int fd,char *buffer,int lenght);
void (*accept_cb)(int fd,char *buffer,int lenght);
}
struct reactor{
int epfd;
}
// thread --> fd
void *routine(void *arg) {
int clientfd = *(int *)arg;
while (1) {
unsigned char buffer[BUFFER_LENGTH] = {0};
int ret = recv(clientfd, buffer, BUFFER_LENGTH, 0);
if (ret == 0) {
close(clientfd);
break;
}
printf("buffer : %s, ret: %d\n", buffer, ret);
ret = send(clientfd, buffer, ret, 0); //
}
}
// socket -->
// bash --> execve("./server", "");
//
// 0, 1, 2
// stdin, stdout, stderr
int main() {
// block
int listenfd = socket(AF_INET, SOCK_STREAM, 0); //
if (listenfd == -1) return -1;
// listenfd
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(9999);
if (-1 == bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) {
return -2;
}
#if 0 // nonblock
int flag = fcntl(listenfd, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(listenfd, F_SETFL, flag);
#endif
listen(listenfd, 10);
#if 0
// int
struct sockaddr_in client;
socklen_t len = sizeof(client);
int clientfd = accept(listenfd, (struct sockaddr*)&client, &len);
unsigned char buffer[BUFFER_LENGTH] = {0};
int ret = recv(clientfd, buffer, BUFFER_LENGTH, 0);
if (ret == 0) {
close(clientfd);
}
printf("buffer : %s, ret: %d\n", buffer, ret);
ret = send(clientfd, buffer, ret, 0); //
//printf("sendbuffer : %d\n", ret);
#elif 0
while (1) {
struct sockaddr_in client;
socklen_t len = sizeof(client);
int clientfd = accept(listenfd, (struct sockaddr*)&client, &len);
pthread_t threadid;
pthread_create(&threadid, NULL, routine, &clientfd);
//fork();
}
#else
fd_set rfds, wfds, rset, wset;
FD_ZERO(&rfds);
FD_SET(listenfd, &rfds);
FD_ZERO(&wfds);
int maxfd = listenfd;
unsigned char buffer[BUFFER_LENGTH] = {0}; // 0
int ret = 0;
// int fd,
while (1) {
rset = rfds;
wset = wfds;
int nready = select(maxfd+1, &rset, &wset, NULL, NULL);
if (FD_ISSET(listenfd, &rset)) {
printf("listenfd --> \n");
struct sockaddr_in client;
socklen_t len = sizeof(client);
int clientfd = accept(listenfd, (struct sockaddr*)&client, &len);
FD_SET(clientfd, &rfds);
if (clientfd > maxfd) maxfd = clientfd;
}
int i = 0;
for (i = listenfd+1; i <= maxfd;i ++) {
if (FD_ISSET(i, &rset)) { //
ret = recv(i, buffer, BUFFER_LENGTH, 0);
if (ret == 0) {
close(i);
FD_CLR(i, &rfds);
} else if (ret > 0) {
printf("buffer : %s, ret: %d\n", buffer, ret);
FD_SET(i, &wfds);
}
} else if (FD_ISSET(i, &wset)) {
ret = send(i, buffer, ret, 0); //
FD_CLR(i, &wfds); //
FD_SET(i, &rfds);
}
}
//
}
#endif
#else
int epollfd = epoll_create(1);
//一个epollfd就是一个epoll池,一个用户可以创建多个epoll池
// reactor就是多个epoll池
struct epoll_event ev;
ev.events =EPOLLIN; //可读 存事件的性质
ev.data.fd = listenfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
// epfd 操作 EPOLL_CTL_DEL 删除,监听fd,事件
while (1)
{
int nready = epoll_wait(epfd,enents,EVNETS_LENGTH,0);
/* 最后一个是返回事件, -1就是一直阻塞,
有数据才返回 0就是立即返回
单位:毫秒 1s = 1000ms
nready 没数据返回0,有数据返回数据量
*/
int i = 0;
for(i = 0; i < nready; i++) {
int clientfd = events[i].data.fd;
if(listenfd == clientfd){ //accept
第一个要单独处理
struct sockaddr_in client;
socklen_t len = sizeof(client);
int connfd = accept(listenfd, (struct sockaddr*)&client, &len);
ev.events = EPOLLIN;
ev.data.fd = connfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
// ev是存储fd信息,ctl是将事件与操作绑定
}else{
char buffer[BUFFER_LENGTH] = {0};
int n= recv(clienfd,buffer,BUFFER_LENGTH,0);
if(n > 0){
buffer[n] = '\0';
printf("recv: %s,n: %d\n",buffer,n);
int j = 0;
for(j = 0;j<BUFFER_LENGTH;j++){
buffer[j] = 'A';
}
/*
while(1){
int sent = send(clientfd,buffer,BUFFER_LENGTH);
// 当协议栈的缓冲区满了的时候,是穿不进去的
// 所以send之前需要判断一下。
}
*/
while(1) {
int sent = send(clientfd, buffer, BUFFER_LENGTH, 0); //
printf("sent: %d\n", sent);
if (sent != BUFFER_LENGTH) break;
}
}
}
}
#endif
}