背景
使用socket完成linux下服务端与客户端的编写,传输层使用tcp协议,服务端使用多进程实现并发。
代码
服务端
Server.h
#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <net/if.h>
enum ServerType{
TCP,
UDP,
};
class Server{
public:
Server();
~Server();
bool start(int domain, ServerType serverType, short int port);
bool stop();
private:
int listenfd;
sockaddr_in addr;
};
Server.cpp
#include "Server.h"
Server::Server(){
listenfd = -1;
}
Server::~Server()
{
}
bool Server::start(int domain, ServerType serverType, short int port)
{
addr.sin_family = domain;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(port);
if(serverType == ServerType::TCP){
listenfd = socket(domain, SOCK_STREAM, 0);
}
else{
listenfd = socket(domain, SOCK_DGRAM, 0);
}
if(listenfd < 0){
printf("create socket error\n");
return false;
}
if(bind(listenfd, (sockaddr *)&addr, sizeof(addr)) < 0){
printf("bind error: %d, %s\n", listenfd, strerror(errno));
return false;
}
if(listen(listenfd, 5) < 0){
printf("listen error\n");
return false;
}
printf("successfully create server\n");
while(1){
sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int client_fd = accept(listenfd, (sockaddr *)&client_addr, &len);
if(client_fd < 0){
printf("invalid client fd: %d, %s\n", client_fd, strerror(errno));
continue;
}
pid_t t= fork();
printf("fork\n");
if(t > 0){ // 主进程
close(client_fd);
continue;
}
else if(t == 0){ // 子进程
close(listenfd);
char buf[1024] = {0};
if(recv(client_fd, buf, sizeof(buf), 0) > 0){
printf("recv message: %s from client!\n", buf);
}
close(client_fd);
exit(0);
}
}
return true;
}
bool Server::stop()
{
if(listenfd > 0){
close(listenfd);
}
return true;
}
tcpServer.cpp
#include "Server.h"
int main(){
Server tcpServer;
tcpServer.start(AF_INET, TCP, 5005);
return 0;
}
客户端
Client.h
#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <net/if.h>
enum ServerType{
TCP,
UDP,
};
class Client{
public:
Client();
~Client();
bool create(int domain, ServerType serverType);
bool connectServer(int domain, const char *ip, short int port);
bool sendMsg(const char *msg);
bool closeClient();
private:
int client_fd;
sockaddr_in addr;
};
Client.cpp
#include "Client.h"
Client::Client(){
client_fd = -1;
}
Client::~Client(){
}
bool Client::create(int domain, ServerType serverType){
if(serverType == ServerType::TCP){
client_fd = socket(domain, SOCK_STREAM, 0);
}
else{
client_fd = socket(domain, SOCK_DGRAM, 0);
}
if(client_fd < 0){
printf("failed to create client\n");
return false;
}
}
bool Client::connectServer(int domain, const char *ip, short int port){
if(client_fd < 0){
return false;
}
sockaddr_in server_addr;
server_addr.sin_family = domain;
server_addr.sin_addr.s_addr = inet_addr(ip);
server_addr.sin_port = htons(port);
if(connect(client_fd, (sockaddr *)&server_addr, sizeof(server_addr)) < 0){
printf("failed to connect to server\n");
return false;
}
}
bool Client::sendMsg(const char *msg){
if(send(client_fd, msg, strlen(msg), 0) < 0){
printf("send msg error\n");
return false;
}
return true;
}
bool Client::closeClient(){
if(client_fd > 0){
close(client_fd);
}
}
tcpClient.cpp
#include "Client.h"
int main(){
Client tcpClient;
tcpClient.create(AF_INET, TCP);
tcpClient.connectServer(AF_INET, "127.0.0.1", 5005);
tcpClient.sendMsg("hello world");
tcpClient.closeClient();
return 0;
}
遇到的问题
服务端accept调用出现错误:invalid client fd: -1, Bad file descriptor
问题分析,子进程中关闭了listenfd,但是没有调用exit退出子进程,导致子进程执行while循环,继续调用accept函数,但此时子进程中的listenfd已经关闭,所以出现Bad file descriptor错误