linux网络编程-----非阻塞connect

libevent在内部由于使用io多路复用函数进行的事件监控,所以它所有的io读写操作都是非阻塞的,换句话说,libevent提供的接口函数evconnlistener_new_bind()和bufferevent_socket_connect()在内部使用socket创建套接字,会默认将其变为非阻塞(使用fcntl可以实现)。

比较特殊的就是客户端的connect函数,因为套接字变为非阻塞,所以connect会立即返回,这就可能出现以下三种情况

  • 连接立即建立,返回0
  • 正在进行三路握手,返回-1,errno设置为EINPROGRESS
  • 连接失败,返回-1,errno不为EINPROGRESS

非阻塞的connect可以节省三路握手的时间,可以运行其它的任务,不需要等待三路握手完成。
所以判断什么时候连接建立完成是很重要的,判断的依据是套接字的可读/可写状态

  • 当连接成功建立时,描述符变为可写
  • 当连接建立遇到错误时,描述符变为既可读又可写

这两个规则很好理解,

  • 当连接成功建立,tcp的缓冲区为空,客户端就可以使用send/write等io函数向套接字描述符写入数据,就是说描述符变为可写
  • 当连接建立失败,tcp的缓冲区仍然可写,但是因为出现错误,所以缓冲区中存在着连接错误信息,所以既可读又可写

有以下几种方法可以达到对connect连接的判断

  • 使用select对客户端套接字进行监听,同时设置超时时长。这种方法主要用于减少connect默认的超时时长,当select返回后,可以判断套接字描述符是否处在读/写事件集中。
  • 既然libevent是统一事件源,所以连接的建立也是一个事件,将套接字描述符添加到event监控中,当可读/可写时就可以判断连接情况
  • 上述两种方法都可以在io多路复用函数返回后通过getsockopt获取套接字SO_ERROR选项,当结果为0时表示连接建立,否则失败
/* 结果存入error中 */
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
<think>好的,我现在需要帮助用户查找在Linux环境下使用C语言实现TCP网络编程的教程和示例代码。用户提供了几个引用,看起来是关于Socket编程的,我需要结合这些引用和我的知识来组织一个清晰的回答。 首先,我需要回忆一下TCP编程的基本步骤。通常分为服务端和客户端。服务端的步骤是创建socket,绑定地址,监听连接,接受连接,然后进行通信。客户端则是创建socket,连接到服务端,然后通信。关闭连接是双方都需要做的。 接下来,看看用户提供的引用内容。引用[4]详细说明了服务端的步骤:socket(), bind(), listen(), accept(), read()/write(), close()。客户端部分在引用[3]中提到了socket、填写服务端地址、connect,封装成函数。引用[2]中的示例代码是关于IP地址转换的,可能对理解地址处理有帮助。引用[1]提到用户之前写过C语言实现TCP连接的文章,但现在想重新整理。 我需要将这些信息整合成一个教程,分步骤说明服务端和客户端的实现,并给出示例代码。同时,需要确保代码符合C语言在Linux下的标准,使用正确的头文件和函数调用。例如,服务端代码可能包括创建socket,设置地址结构体,绑定,监听,接受连接,读写数据。客户端则是创建socket,连接,然后通信。 另外,需要注意错误处理,比如检查每个系统调用的返回值,这在示例代码中可能需要简化,但用户可能需要知道实际应用中应该处理错误。例如,socket()返回-1表示失败,bind()也可能出错,需要处理。 然后,用户要求生成相关问题。我需要考虑用户可能对TCP编程的哪些方面有疑问,比如多客户端处理、阻塞非阻塞、错误处理等。可能的问题包括如何同时处理多个客户端连接,如何设置超时,或者TCP与UDP的区别等。 最后,确保回答结构清晰,分步骤说明,代码部分用代码块表示,并正确引用用户提供的引用来源。例如,服务端的步骤引用[4],客户端的函数封装引用[3],IP转换的示例引用[2]。 现在需要组织这些内容,确保符合用户给出的格式要求,如使用中文,行内公式正确(虽然这里可能用不到),独立公式单独成段。不过这里主要是代码和步骤说明,所以重点放在代码的正确性和步骤的清晰性上。 可能还需要验证示例代码是否正确。比如,服务端代码是否正确使用socket(), bind(), listen(), accept()等函数,客户端是否正确使用connect()。确保示例代码中的函数参数正确,比如地址结构体使用struct sockaddr_in,正确设置地址族、端口、IP地址,并使用htonl, htons等函数转换字节序。 总之,步骤是:概述TCP编程步骤,分服务端和客户端,给出示例代码,引用相关引用,然后生成相关问题。</think>### Linux C语言TCP网络编程实现教程 在Linux环境下使用C语言实现TCP通信,主要分为**服务端**和**客户端**两部分。以下是具体步骤和示例代码: --- #### **服务端实现步骤** [^4] 1. **创建Socket**:使用`socket()`函数创建套接字。 2. **绑定地址**:通过`bind()`将套接字与本地IP和端口绑定。 3. **监听连接**:调用`listen()`进入监听状态,等待客户端连接。 4. **接受连接**:使用`accept()`接收客户端请求,返回新的通信套接字。 5. **数据交互**:通过`read()`/`write()`进行通信。 6. **关闭连接**:使用`close()`释放资源。 **示例代码(服务端)**: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 8080 int main() { int server_fd, new_socket; struct sockaddr_in address; int addrlen = sizeof(address); char buffer[1024] = {0}; // 1. 创建Socket if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // 2. 绑定地址 if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 3. 监听连接 if (listen(server_fd, 3) < 0) { perror("listen failed"); exit(EXIT_FAILURE); } // 4. 接受连接 if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) { perror("accept failed"); exit(EXIT_FAILURE); } // 5. 数据交互 read(new_socket, buffer, 1024); printf("Received: %s\n", buffer); send(new_socket, "Hello from server", 17, 0); // 6. 关闭连接 close(new_socket); close(server_fd); return 0; } ``` --- #### **客户端实现步骤** [^3] 1. **创建Socket**:使用`socket()`函数。 2. **设置服务端地址**:填充服务端IP和端口。 3. **发起连接**:调用`connect()`连接到服务端。 4. **数据交互**:通过`send()`/`recv()`通信。 5. **关闭连接**:释放资源。 **示例代码(客户端)**: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define SERVER_IP "127.0.0.1" #define PORT 8080 int main() { int sock = 0; struct sockaddr_in serv_addr; char buffer[1024] = {0}; // 1. 创建Socket if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); // 2. 转换IP地址格式(参考引用[2]) if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) { perror("invalid address"); exit(EXIT_FAILURE); } // 3. 发起连接 if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { perror("connection failed"); exit(EXIT_FAILURE); } // 4. 数据交互 send(sock, "Hello from client", 17, 0); read(sock, buffer, 1024); printf("Server response: %s\n", buffer); // 5. 关闭连接 close(sock); return 0; } ``` --- #### **关键函数说明** - `socket()`:创建套接字,参数`AF_INET`表示IPv4,`SOCK_STREAM`表示TCP协议。 - `bind()`:绑定IP和端口时需注意字节序转换(如`htons()`)[^2]。 - `accept()`:阻塞等待客户端连接,返回新的套接字用于通信。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值