1.TCPsocket
TCPsocket是指使用传输控制协议(TCP)的网络套接字。套接字是网络中两台计算机之间进行通信的端点。TCP是一种可靠的、面向连接的协议,提供了错误检测、流量控制和拥塞控制等功能。
TCPsocket通常用于客户端-服务器通信,其中客户端程序通过TCP/IP网络连接到服务器程序。客户端和服务器可以通过TCP套接字发送和接收TCP数据包来交换数据。
2.Linux中的TCPsocket
在Linux中,TCPsocket是一种在网络编程中使用的套接字,用于建立TCP连接并进行数据交换。
在Linux中,使用C语言编写网络程序通常涉及到以下的系统调用和函数来创建和使用TCPsocket:
-
socket()函数:用于创建一个新的套接字,返回一个套接字描述符(文件描述符)。
-
bind()函数:将一个本地地址绑定到套接字。
-
listen()函数:将套接字设置为监听状态,接受客户端的连接请求。
-
accept()函数:接受客户端的连接请求,并返回一个新的套接字描述符,用于与客户端进行通信。
-
connect()函数:用于与远程服务器建立TCP连接。
-
send()和recv()函数:用于发送和接收数据。
-
close()函数:关闭套接字连接。
通过这些函数,可以在Linux中创建TCPsocket并进行数据通信。在编程中,可以使用套接字描述符进行读取和写入操作,来发送和接收数据。
当使用TCPsocket进行网络编程时,以下是对这些函数接口的详细介绍:
socket()函数:创建一个新的套接字,返回一个套接字描述符(file descriptor)。函数原型为:
c
int socket(int domain, int type, int protocol);
- domain参数指定套接字的地址族(address family),如AF_INET表示IPv4地址族、AF_INET6表示IPv6地址族。
- type参数指定套接字的类型,如SOCK_STREAM表示TCP套接字、SOCK_DGRAM表示UDP套接字。
- protocol参数指定套接字使用的协议,通常选择0,由操作系统根据套接字类型自动选择合适的协议。
bind()函数:将一个本地地址绑定到套接字。函数原型为:
c
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- sockfd参数为套接字描述符。
- addr参数为要绑定的本地地址,通常使用结构体sockaddr_in(IPv4)或sockaddr_in6(IPv6)来表示。
- addrlen参数为addr结构体的大小。
listen()函数:将套接字设置为监听状态,接受客户端的连接请求。函数原型为:
c
int listen(int sockfd, int backlog);
- sockfd参数为套接字描述符。
- backlog参数指定等待连接队列的最大长度。
accept()函数:接受客户端的连接请求,并返回一个新的套接字描述符,用于与客户端进行通信。函数原型为:
c
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- sockfd参数为监听套接字描述符。
- addr参数为指向用于存储客户端地址的结构体指针。
- addrlen参数为指向addr结构体大小的指针。
connect()函数:用于与远程服务器建立TCP连接。函数原型为:
c
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- sockfd参数为套接字描述符。
- addr参数为远程服务器的地址。
- addrlen参数为addr结构体的大小。
send()和recv()函数:用于发送和接收数据。函数原型分别为:
c
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
- sockfd参数为套接字描述符。
- buf参数为数据的缓冲区。
- len参数为数据的长度。
- flags参数为可选的标志,如0表示无特殊选项。
close()函数:关闭套接字连接。函数原型为:
c
int close(int sockfd);
- sockfd参数为套接字描述符。
cpp
#include <iostream>
#include <string>
#include <functional>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "InetAddr.hpp"
#include "Log.hpp"
#include "ThreadPool.hpp"
enum
{
SOCKET_ERROR = 1,
BIND_ERROR,
LISTEN_ERROR,
USAGE_ERROR
};
const static int defaultsockfd = -1;
const static int gbacklog = 16;
class TcpServer;
class ThreadData
{
public:
ThreadData(int fd, InetAddr addr, TcpServer *s):sockfd(fd), clientaddr(addr), self(s)
{}
public:
int sockfd;
InetAddr clientaddr;
TcpServer *self;
};
using task_t = std::function<void()>;
class TcpServer
{
public:
TcpServer(int port) : _port(port), _listensock(defaultsockfd), _isrunning(false)
{
}
void InitServer()
{
// 1. 创建流式套接字
_listensock = ::socket(AF_INET, SOCK_STREAM, 0);
if (_listensock < 0)
{
LOG(FATAL, "socket error");
exit(SOCKET_ERROR);
}
LOG(DEBUG, "socket create success, sockfd is : %d\n", _listensock);
// 2. bind
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = INADDR_ANY;
int n = ::bind(_listensock, (struct sockaddr *)&local, sizeof(local));
if (n < 0)
{
LOG(FATAL, "bind error");
exit(BIND_ERROR);
}
LOG(DEBUG, "bind success, sockfd is : %d\n", _listensock);
// 3. tcp是面向连接的,所以通信之前,必须先建立连接。服务器是被连接的
// tcpserver 启动,未来首先要一直等待客户的连接到来
n = ::listen(_listensock, gbacklog);
if (n < 0)
{
LOG(FATAL, "listen error");
exit(LISTEN_ERROR);
}
LOG(DEBUG, "listen success, sockfd is : %d\n", _listensock);
}
void Service(int sockfd, InetAddr client)
{
LOG(DEBUG, "get a new link, info %s:%d, fd : %d\n", client.Ip().c_str(), client.Port(), sockfd);
std::string clientaddr = "[" + client.Ip() + ":" + std::to_string(client.Port()) + "]# ";
while (true)
{
char inbuffer[1024];
ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
if (n > 0)
{
inbuffer[n] = 0;
std::cout << clientaddr << inbuffer << std::endl;
std::string echo_string = "[server echo]# ";
echo_string += inbuffer;
write(sockfd, echo_string.c_str(), echo_string.size());
}
else if (n == 0)
{
// client 退出&&关闭连接了
LOG(INFO, "%s quit\n", clientaddr.c_str());
break;
}
else
{
LOG(ERROR, "read error\n", clientaddr.c_str());
break;
}
sleep(5);
break;
}
std::cout << "server开始退出" << std::endl;
shutdown(sockfd, SHUT_RD);
std::cout << "shut _ rd " << std::endl;
sleep(10);
//shutdown(sockfd, SHUT_WR);
//std::cout << "shut _ wr " << std::endl;
//::close(sockfd); // 文件描述符泄漏
}
static void *HandlerSock(void *args)
{
pthread_detach(pthread_self());
ThreadData *td = static_cast<ThreadData *>(args);
td->self->Service(td->sockfd, td->clientaddr);
delete td;
return nullptr;
}
void Loop()
{
_isrunning = true;
// 4. 不能直接接受数据,先获取连接
while (_isrunning)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sockfd = ::accept(_listensock, (struct sockaddr *)&peer, &len);
if (sockfd < 0)
{
LOG(WARNING, "accept error\n");
continue;
}
// Version 0 : 一次只能处理一个请求 --- 不可能
// Service(sockfd, InetAddr(peer));
// Version 1: 采用多进程
// pid_t id = fork();
// if (id == 0)
// {
// // child : 关心sockfd, 不关心listensock
// ::close(_listensock); // 建议
// if(fork() > 0) exit(0);
// Service(sockfd, InetAddr(peer)); //孙子进程 -- 孤儿进程 --- 系统领养
// exit(0);
// }
// // father: 关心listensock,不关心sockfd
// ::close(sockfd);
// waitpid(id, nullptr, 0);
// version 2: 采用多线程
pthread_t t;
ThreadData *td = new ThreadData(sockfd, InetAddr(peer), this);
pthread_create(&t, nullptr, HandlerSock, td); //将线程分离
// vesion 3: 采用线程池
// task_t t = std::bind(&TcpServer::Service, this, sockfd, InetAddr(peer));
// ThreadPool<task_t>::GetInstance()->Enqueue(t);
}
_isrunning = false;
}
~TcpServer()
{
if (_listensock > defaultsockfd)
::close(_listensock);
}
private:
uint16_t _port;
int _listensock;
bool _isrunning;
};
cpp
#include <iostream>
#include <string>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
void Usage(std::string proc)
{
std::cout << "Usage:\n\t" << proc << " serverip serverport\n"
<< std::endl;
}
// ./tcp_client serverip serverport
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(1);
}
std::string serverip = argv[1];
uint16_t serverport = std::stoi(argv[2]);
int sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
std::cerr << "socket error" << std::endl;
exit(2);
}
// tcp client 要bind,不要显示的bind.
struct sockaddr_in server;
// 构建目标主机的socket信息
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(serverport);
server.sin_addr.s_addr = inet_addr(serverip.c_str());
int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
if (n < 0)
{
std::cerr << "connect error" << std::endl;
exit(3);
}
while(true)
{
std::cout << "Please Enter# ";
std::string outstring;
std::getline(std::cin, outstring);
ssize_t s = send(sockfd, outstring.c_str(), outstring.size(), 0); //write
if(s > 0)
{
char inbuffer[1024];
ssize_t m = recv(sockfd, inbuffer, sizeof(inbuffer)-1, 0);
if(m > 0)
{
inbuffer[m] = 0;
std::cout << inbuffer<< std::endl;
}
else
{
break;
}
}
else
{
break;
}
}
//shutdown(sockfd, SHUT_WR);
::close(sockfd);
return 0;
}