C++网络编程之socket
2016-09-19 17:41
459 查看
1. server端代码如下:
#include <signal.h> #include <unistd.h> #include <arpa/inet.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <iostream> #include <string.h> using namespace std; int connected = 1; void reset(int) { connected = 0; } int main(int argc, char* arg[]) { int socket_fd = 0; if((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { cout<<strerror(errno)<<endl; exit(1); } struct sockaddr_in my_addr; const int PORT = 3333; my_addr.sin_family = AF_INET; my_addr.sin_port = htons(PORT); my_addr.sin_addr.s_addr = INADDR_ANY; bzero(&(my_addr.sin_zero), 8); if(bind(socket_fd, (struct sockaddr*)(&my_addr), sizeof(sockaddr)) == -1) { cout<<strerror(errno)<<endl; exit(1); } const int BACKLOG = 10; if(listen(socket_fd, BACKLOG) == -1) { cout<<strerror(errno)<<endl; exit(1); } socklen_t sin_size; int client_fd = 0; struct sockaddr_in remote_addr; int len = 0; sin_size = sizeof(struct sockaddr_in); const int BUFFER_LEN = 4096; char buffer[BUFFER_LEN]; while(1) { if((client_fd = accept(socket_fd, (sockaddr*)(&remote_addr), &sin_size)) == -1) { cout<<strerror(errno)<<endl; continue; } connected = 1; while(connected) { len = recv(client_fd, buffer, sizeof(buffer), 0); cout<<buffer; memset(buffer, 0, sizeof(buffer)); memcpy(buffer, "done\n", 5); send(client_fd, buffer, strlen(buffer), 0); signal(SIGPIPE, reset); memset(buffer, 0, sizeof(buffer)); } close(client_fd); } close(socket_fd); return 0; }
2. client端代码如下:
#include <unistd.h> #include <arpa/inet.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <string.h> using namespace std; int main(int argc, char* argv[]) { int client_fd = 0; if((client_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { cout<<strerror(errno)<<endl; exit(1); } const int PORT = 3333; struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if(connect(client_fd, (struct sockaddr*)(&server_addr), sizeof(server_addr)) < 0) { cout<<strerror(errno)<<endl; exit(1); } const int BUFFER_LEN = 4096; char send_buffer[BUFFER_LEN]; char rec_buffer[BUFFER_LEN]; while(fgets(send_buffer, sizeof(send_buffer), stdin) != NULL) { send(client_fd, send_buffer, strlen(send_buffer), 0); recv(client_fd, rec_buffer, sizeof(rec_buffer), 0); cout<<rec_buffer; memset(send_buffer, 0, sizeof(send_buffer)); memset(rec_buffer, 0, sizeof(rec_buffer)); } close(client_fd); return 0; }
3. 出现的问题分析:
3.1.
当结束掉客户端进程时,服务端进程也会退出。gdb调试后发现,服务端进程在客户端进程结束后收到了SIGPIPE的信号。
原因分析:
对一个对端已经关闭的socket执行两次write,第二次会产生SIGPIPE信号,该信号默认结束进程。这符合TCP的四次握手关闭。因为TCP/IP协议是全双工的协议,双方都需要发送FIN/ACK报文并且收到ACK才算成功关闭了socket连接。客户端进程结束只是结束了其中一个方向上的通道,此时服务端向关闭的socket执行send第一次会收到RST报文,第二次则会由操作系统发出SIGPIPE信号,而SIGPIPE信号默认会结束进程。
解决方案:
将服务端的send和客户端的recv注释掉后解决掉了3.1.的问题,但是当客户端进程结束之后,服务端程序死掉,必须重启才能与新的客户端进行连接。原因是注释掉服务端的send和客户端的recv之后,服务端在客户端进程结束之后无法得知客户端进程已经结束,仍然阻塞在recv上。
捕获并且忽略SIGPIPE信号。仍然存在和第一种方案同样的问题。
捕获并且自定义对SIGPIPE信号的处理函数,使得获取SIGPIPE信号时跳出当前循环,重新尝试建立socket连接。代码修改见上文中代码中与变量connected和函数reset有关的部分。
相关文章推荐
- C++网络编程 如何使用SOCKET 发送HTTP1.1 GET POST请求包
- C++网络编程 如何使用SOCKET 发送HTTP1.1 GET POST请求包
- 【网络SOCKET编程】DEV-C/C++ 编译时出现undefined reference to `WSAStartup@8'等错误解决办法
- c++ 网络编程 socket 聊天客户端/服务器
- 【C++】winsocket网络编程实例
- C++ 网络编程之使用socket + epoll 模拟http 的请求与响应
- 学习C/C++下的socket网络编程
- 网络编程 C++ ———MFC Socket
- 网络编程 C++ ———MFC Socket
- C++中Socket网络编程实例详解
- C++网络编程之Socket编程
- linux下C/C++网络编程基本:socket实现tcp和udp的例子
- c++网络编程之socket
- Linux C++/Java/Web/OC Socket网络编程
- Linux下Socket网络接口编程(C++)
- 书评:C++网络编程,卷2 by Matthew Wilson
- Beej网络socket编程指南(转贴)
- 转:网络Socket 编程参考教学篇
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(二)----使用方法
- 网络编程 socket