传输层协议、有连接、可靠传输、面向字节流
使用recv函数接受信息,send函数发送信息,返回值类型都为size_t ;
客户端停止发送信息后,服务器要把当前通信套接字关闭掉;在提供服务的时候,当前服务结束,也要把通信套接字关闭掉。
#define BACKLOG 5
struct tcpServer{
private:
int port;
int lsock; //监听套接字
public:
tcpServer(int _port):port(_port), lsock(-1){
}
void initServer(){ //初始化Server:创建---绑定---监听服务器端套接字
lsock = socket(AF_INET, SOCK_STREAM, 0);
if(lsock < 0){
std::cout << "socket error!\n" << std::endl;
exit(2);
}else{
std::cout << "lsock: " << lsock << std::endl;
}
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(lsock, (struct sockaddr*)&local, sizeof(local)) < 0){
std::cout << "bind error!\n" << std::endl;
exit(3);
}
if(listen(lsock, BACKLOG) < 0){ //BACKLOG为底层全连接的数量
std::cout << "listen error!\n" << std::endl;
exit(4);
}
}
void service(int sock){ //接受客户端发送的请求,并返回应答信息
char msg[1024];
while(true){
ssize_t s = recv(sock, msg, sizeof(msg)-1, 0);
if(s > 0){ //接受信息成功,显示收到的信息,并发送应答
msg[s] = 0;
std::cout << "client# " << msg << std::endl;
std::string echo = msg;
echo += " [server echo!] ";
send(sock, echo.c_str(), echo.size(), 0);
}
else if(s == 0){
std::cout << "client quit..." << std::endl;
close(sock); //客户端停止发送信息后把套接字关闭掉
break;
}
else{
std::cout << "recv client data error.." << std::endl;
break;
}
}
close(sock);
}
void start(){
struct sockaddr_in end_point;
socklen_t len = sizeof(end_point);
//tcpServer一旦开始工作,就一直等待着接受客户端发送建立连接的请求
while(true){
int sock = accept(lsock, (struct sockaddr*)&end_point, &len);
if(sock < 0){ //lsock:获取新链接 sock:服务新链接
std::cout << "accept error!\n" << std::endl;
continue;
}
std::cout << "get a new link...\n" << std::endl;
service(sock);
}
}
~tcpServer(){
close(lsock);
}
};
以命令行参数的形式传入创建tdp服务器所需要的端口号参数。
void Usage(std::string proc){
std::cout << "Usage: " << proc << "local_port" << std::endl;
}
int main(int argc, char *argv[]){
if(argc != 2){
Usage(argv[0]);
exit(1);
}
tcpServer *ts = new tcpServer(atoi(argv[1]));
ts->initServer();
ts->start();
delete ts;
return 0;
}
connect()申请建立连接,返回值不为0时,表示申请失败。
struct tcpClient{
private:
std::string svr_ip;
int svr_port;
int sock; //连接套接字
public:
tcpClient(std::string _ip, int _port)
:svr_ip(_ip), svr_port(_port){
}
void initClient(){
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
std::cout << "socket error!\n" << std::endl;
exit(2);
}
struct sockaddr_in svr;
svr.sin_family = AF_INET;
svr.sin_port = htons(svr_port);
svr.sin_addr.s_addr = inet_addr(svr_ip.c_str());
if(connect(sock, (struct sockaddr*)&svr, sizeof(svr)) != 0){ //不为0时,请求连接失败
std::cout << "connect error!\n" << std::endl;
exit(3);
}
}
void start(){
char msg[1024];
while(true){
std::cout << "Please Enter Message# " << std::endl;
size_t s = read(0, msg, sizeof(msg)-1); //从标准输入流中读取信息
if(s > 0){ //输入信息成功
msg[s-1] = 0;
send(sock, msg, sizeof(msg)-1, 0); //发送信息
size_t ss = recv(sock, msg, sizeof(msg)-1, 0); //接受应答信息
if(ss > 0){
msg[ss] = 0;
std::cout << "server echo# \n" << msg << std::endl;
}
}
}
}
~tcpClient(){
close(sock);
}
};
以命令行参数的形式传入创建tdp客户端所需要的ip地址参数和端口号参数。
void Usage(std::string proc){
std::cout << "Usage: " << proc << "srv_ip, srv_port" << std::endl;
}
int main(int argc, char *argv[]){
if(argc != 3){
Usage(argv[0]);
exit(1);
}
tcpClient *tc = new tcpClient(argv[1], atoi(argv[2]));
tc->initClient();
tc->start();
delete tc;
return 0;
}
初始版本的tcp程序一次只能给一个客户端提供服务,当有其他客户端请求服务器时,可以connect连接上,但是无法通信,因为start循环体内的语句限制了只要当前客户端不退出,就要一直为这一个客户端服务。
pid_t id = fork();
if(id == 0){ //子进程
if(fork() > 0){ //子进程退出,留下孙子进程:避免出现僵尸进程
exit(0);
}
//让孙子进程处理IO服务
close(lsock);
service(sock);
exit(0);
}
close(sock); //关闭IO套接字
waitpid(id, NULL, 0); //回收子进程的资源
pthread_t tid;
pthread_create(&tid, nullptr, serviceRoutine, (void*)&sock); //通信套接字的地址传参
static void *serviceRoutine(void *arg){
pthread_detach(pthread_self()); //为了避免线程等待,我们使用线程分离
std::cout << "create a new thread for IO..." << std::endl;
int *p = (int*)arg;
int sock = *p;
service(sock);
delete p;
}
线程池版本的TCP网络程序----相关实现代码点此处获取!!!