2 Star 11 Fork 5

张奇峰 / c_tcp_udp

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
tcp_server.c 6.71 KB
一键复制 编辑 原始数据 按行查看 历史
/*tcp服务器,基于epoll(lt)模型实现,简单易懂,也是 epoll 模型的默认模型*/
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <errno.h>
#ifndef STR_CONV
#include "./libs/functions.h"
#endif
#ifndef ERRORS
#include "./errors.c"
#endif
#define CLIENT_CONN_MAX_NUM 8192
const int PORT = 9505;
const char SERVER_IP[15] = {"0.0.0.0"};
int main() {
//TCP 参数: SOCK_STREAM、IPPROTO_TCP ;UDP参数: SOCK_DGRAM 、IPPTOTO_UDP
int server_socket = -1, client_socket = -1;
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
printf("%s\n", TCP_SOCK_INIT_FAIL);
return -1;
}
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_size;
int received_data_len;
char buffer[BUFSIZ];//缓冲区大小
int sockopt = 0;
sockopt = SO_REUSEADDR;
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &server_socket, sizeof(server_socket));
//对内存清零
memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));
server_addr.sin_family = AF_INET; //选择IPV4地址类型
server_addr.sin_port = htons(PORT); //选择端口号
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); //选择IP地址
int server_addr_len = sizeof(server_addr);
// 绑定
if (bind(server_socket, (struct sockaddr *) &server_addr, server_addr_len) < 0)//绑定套接字
{
printf("%s\n", TCP_SOCK_BIND_FAIL);
return -1;
}
//监听
if (listen(server_socket, SOMAXCONN) < 0)//调用listen对指定端口进行监听
{
printf("%s\n", TCP_SOCK_LISTEN_FAIL);
return -1;
}
//epoll 事件监听,所有连接的tcp客户端都会触发同一个事件函数(epoll结构体的属性值会改变)
struct epoll_event event;
//存储所有的客户端,包括服务端
struct epoll_event wait_event_list[CLIENT_CONN_MAX_NUM];
int j = 0;
int epoll_fd = epoll_create(64);
if (epoll_fd == -1) {
printf("%s\n", TCP_CREATE_EPOLL_FAIL);
return -1;
}
event.events = EPOLLIN;//可读事件
event.data.fd = server_socket;//server_socket
// 将服端 socketserver注册在epoll模型中,epoll就开始监听 socket 文件的变化
int result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_socket, &event);
if (result == -1) {
printf("%s\n", TCP_SERVERSOCK_REGISTER_EPOLL_FAIL);
return -1;
}
while (1) {
errno = 0;
// 阻塞监听当前连接的socket套接字状态是否发生变化,有变化就会有返回值,wait_event_list 数组返回的是最近一次变化(活跃)的socket
result = epoll_wait(epoll_fd, wait_event_list, CLIENT_CONN_MAX_NUM, -1);
if (result == 0) {
continue;
} else if (result < 0) {
printf("%s,错误编号:%d,错误详情:%s\n", TCP_EPOLL_WAIT_ERR, errno, strerror(errno));
continue;
}
for (j = 0; j < result; j++) {
//server_socket 监测客户端上线事件
if (server_socket == wait_event_list[j].data.fd && wait_event_list[j].events) {
client_addr_size = sizeof(client_addr);
client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &client_addr_size);
//获取客户端的基本信息,ip、port
printf("Client_IP: %s\n", inet_ntoa(client_addr.sin_addr));
printf("Client_Port: %d\n", htons(client_addr.sin_port));
char msg[] = {"成功连接到tcpserver_epoll_lt!"};
send(client_socket, msg, strlen(msg), 0);
event.events = EPOLLIN;//可读
event.data.fd = client_socket;
result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_socket, &event);
if (result == -1) {
printf("%s,错误信息:%s\n", TCP_CLIENT_REGISTER_FAIL, strerror(errno));
}
continue;
}
//客户端套接字本身有变化(属于客户端消息事件)
if (wait_event_list[j].data.fd != server_socket) {
memset(&buffer, 0, sizeof(buffer));
received_data_len = read(wait_event_list[j].data.fd, buffer, BUFSIZ);
buffer[BUFSIZ] = '\0'; // 最后一位强制设置为结束标记,避免中英文混合发的时候数据对不齐,造成最后一位出现不确定字符
char tmp_out_res[BUFSIZ] = {0};
// 不是 utf8 编码,那么就当做是 gbk 去处理, 目前就先支持 gbk系列 和 utf8系列
if (!is_utf8_format(buffer)) {
// 以下函数的最后一个参数,输出缓冲区的长度计算依据:在linux系统,gbk编码占2字节,utf8 编码占3字节,也就是说,你输入了一个中文字,那么输出空间的长度就是输入空间的1.5倍,保险起见,我们就直接设置为 2倍
gbk_to_utf8(buffer, received_data_len, tmp_out_res, received_data_len * 2);
if (errno != 0) {
printf("%s,错误编码:%d, 错误信息:%s\n", STRCONV_FAIL, errno, strerror(errno));
continue;
}
}else{
strcpy(tmp_out_res,buffer);
}
switch (wait_event_list[j].events) {
case EPOLLIN:
case EPOLLOUT:
case EPOLLPRI:
if (received_data_len > 0) {
// 向客户端发送消息,原路返回, 注意发送消息的长度应该是实际长度,不能 sizeof(buffer), 这样计算出来是 8192,就会有空数据发送出去
send(wait_event_list[j].data.fd, tmp_out_res, strlen(tmp_out_res), 0);
break;
}
case EPOLLERR:
case EPOLLHUP:
close((wait_event_list[j]).data.fd);
event.data.fd = wait_event_list[j].data.fd;
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, wait_event_list[j].data.fd, &event);
printf("%s,%d,可能的错误:%s\n", UNKNOW_CLIENT_STATUS, wait_event_list[j].events, strerror(errno));
break;
default:
printf("events 事件值未匹配到以上列表,是否发生错误?错误coe:%d, 错误消息:%s\n",errno,strerror(errno));
}
}
}
}
close(server_socket);//关闭套接字
return 0;
}
C
1
https://gitee.com/daitougege/c_tcp_udp.git
git@gitee.com:daitougege/c_tcp_udp.git
daitougege
c_tcp_udp
c_tcp_udp
master

搜索帮助