简单的Tcp服务器
TCP客户端实例程序
文件结构
-
源代码文件目录(src)
这里有main.cpp,是服务器端程序
-
头文件目录(include)
各种.h文件
-
实例程序文件目录(test)
这里写一个客户端连接程序
-
项目构建文件(Makefile)
完成项目的编译
头文件
include/base.h
#ifndef __BASE__H #define __BASE__H #define BUFFSIZE 64 #endif
TCP服务器端
网络编程的主要内容其实就是在服务器端,要实现最基础的 TCP 服务器
src/main.cpp
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
#include <sys/socket.h>
#include "base.h"
using namespace std;
// 错误处理函数
static void error_handle(string opt,string message){
perror(opt.c_str());
cout << message << endl;
exit(1);
}
int main(int argc, char *argv[]){//argc是参数个数
//参数需要2个:运行文件+端口
if(argc < 2){
cout << "less" << argv[0] << "port" << endl;
exit(1);
}
//创建套接字
int serve_sock = socket(PF_INET, SOCK_STREAM, 0);
if(serve_sock==-1){
error_handle("socket", "socket() error");
}
//绑定地址
struct sockaddr_in serve_addr;
serve_addr.sin_family = AF_INET;
//htonl---host to net long
//将一个32位的主机字节序变成网络字节序
serve_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serve_addr.sin_port = htons(atoi(argv[1]));
//返回指针要传地址,而且要强制类型转换成sockaddr*类型
int ret = bind(serve_sock, (struct sockaddr *)&serve_addr, sizeof(serve_addr));
if(ret==-1){
error_handle("bind", "bind() error");
}
//监听
if(listen(serve_sock,5)==-1){
error_handle("listen", "listen() error");
}
//accept--接受客户端的请求
int client_sock;
struct sockaddr_in client_addr;
socklen_t client_addr_size=sizeof(client_addr);
//while(1)循环应答
while(1){
client_sock = accept(serve_sock, (struct sockaddr *)&client_addr, &client_addr_size);
if(client_sock ==-1){
continue;//接受新的请求
}
//接收新的客户端请求,进行客户端数据处理
//inet_ntoa():将一个网络字节序的IP地址转化为点分十进制的IP地址(字符串)。
cout << "Accept New Client : " << inet_ntoa(client_addr.sin_addr) << \
", port : " << ntohs(client_addr.sin_port) << std::endl;
cout << "Start Recv Client Data........." << endl;
/*传输信息*/
char message[BUFFSIZE];
memset((void *)&message, 0, BUFFSIZE);//传指针
int MessageLen;
//读取客户端的请求数据
while((MessageLen=read(client_sock,message,BUFFSIZE))!=0){
cout << "Server recv from Client:" << message << endl;
//将消息回传给客户端
write(client_sock, message, MessageLen);
//清空消息
memset(message, 0, BUFFSIZE);
}
close(client_sock);
}
//服务器关闭
close(serve_sock);
return 0;
}
客户端
test/tcpClient.cpp
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
#include <sys/socket.h>
#include "base.h"
using namespace std;
// 出错调用函数
static void error_handle(std::string opt, std::string message)
{
//根据errno值获取失败原因并打印到终端
perror(opt.c_str());
std::cout << message << std::endl;
exit(1);
}
int main(int argc,char* argv[]){
if(argc < 3){
cout << "Input IP and Port!" << endl;
}
int sock = socket(PF_INET, SOCK_STREAM, 0);
if(sock==-1){
error_handle("socket", "socket() error");
}
//向服务器请求连接
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
int ret =connect(sock,(struct sockaddr *)&serv_addr, sizeof(serv_addr));
if(ret==-1){
error_handle("connect", "connect() error");
}
cout << "connect success!!" << endl;
//数据处理
char message[BUFFSIZE];
while (1){
cout << "Please Input Message(Q to quit) : " << endl;
cin >> message;
if((!strcmp(message, "q")) || (!strcmp(message, "Q"))){
break;
}
//把数据发送给服务端
write(sock,message,strlen(message));
//读取u服务端回传的消息
int len = read(sock, message, BUFFSIZE - 1);
message[len] = 0;
cout << "echo message:" << message << endl;
}
close(sock);
return 0;
}
Makefile
pwd=${shell pwd}
src_dir=$(pwd)/src/
include_dir=$(pwd)/include/
test_dir=$(pwd)/test/
TARGET=TcpServer
########################## src section #################################
obj_src=${shell find $(src_dir) -name "*.cpp"}
OBJS=${patsubst %.cpp,%.o,$(obj_src)}
#添加测试文件的内容
test_src=${shell find ${test_dir} -name "*.cpp"}
TEST_OBJ=${patsubst %.cpp,%.o,${test_src}}
tests=${patsubst %.cpp,%,${test_src}}
####################### flag section ########################################
GXX=g++
CFLAGS=-I${include_dir}
#############################################################################
$(TARGET): $(OBJS)
$(GXX) $(CFLAGS) -o $@ $(OBJS)
%.o:%.cpp
$(GXX) ${CFLAGS} -c -o $@ $<
#添加编译测试文件的规则使用
.PHONY: test
test: $(tests)
$(tests): $(TEST_OBJ)
$(GXX) $(CFLAGS) -o $@ $(TEST_OBJ)
$(TEST_OBJ): $(test_src)
$(GXX) ${CFLAGS} -c $< -o $@
clean:
rm $(TARGET)
rm $(OBJS)
rm ${TEST_OBJ}
rm ${tests}
项目编译
注意如果头文件.h和.cpp在同一目录下可直接编译
如果头文件在同一目录文件夹,则必须在编译时指定头文件位置
😄本项目编译
在当前目录下
make
在当前目录下
make test
make clean
可以去掉执行的文件
运行结果
服务端一次可以多个监听,但是只有一个可以通信
等当前客户端退出后,其他客户端可以与之通信,相当于阻塞