1.tcp特点
①传输层协议
②有连接
③可靠传输
④面向字节流
2.几个常用接口
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
tcp通用服务端(.hpp)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
class TcpSvr
{
public:
TcpSvr()
{
_sock = -1;
}
~TcpSvr()
{
_sock = -1;
}
//创建套接字
bool CreateSock()
{
_sock = socket(AF_INET, SOCK_STREAM, 6);
if (_sock < 0) {
perror("create");
return false;
}
return true;
}
//绑定端口信息(都可以,建议客户端不调用)
//客户端调用后就写死了,就只能与绑定端口进行通信
bool Bind(std::string& ip, uint16_t port)
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
ssize_t ret = bind(_sock, (struct sockaddr*)&addr, sizeof(addr));
if (ret < 0) {
perror("bind");
return false;
}
return true;
}
//监听(服务端)
bool Listen(int Backlog)
{
ssize_t ret = listen(_sock, Backlog);
if (ret < 0) {
perror("listen");
return false;
}
return true;
}
//接收请求(服务端)
bool Accept(TcpSvr& ts, struct sockaddr_in* addr = NULL)
{
struct sockaddr_in peeraddr;
socklen_t len = sizeof(struct sockaddr_in);
int ret = accept(_sock, (struct sockaddr*)&peeraddr, &len);
if (ret < 0) {
perror("accept");
return false;
}
ts._sock = ret;
if (addr != NULL){
memcpy(addr, &peeraddr, len);
}
return true;
}
//建立连接(客户端调用)
bool Connect(std::string& ip, uint16_t port)
{
struct sockaddr_in destaddr;
destaddr.sin_family = AF_INET;
destaddr.sin_port = htons(port);
destaddr.sin_addr.s_addr = inet_addr(ip.c_str());
int ret = connect(_sock, (struct sockaddr*)&destaddr, sizeof(destaddr));
if (ret < 0) {
perror("connect");
return 0;
}
return true;
}
//发送数据
bool Send(std::string& buf)
{
int sendSize = send(_sock, buf.c_str(), buf.size(), 0);
if (sendSize < 0) {
perror("send");
return false;
}
return true;
}
//接收数据
bool Recv(std::string& buffer)
{
char buf[1024] = {0};
//0:阻塞接收
//MSG_PEEK:探测接收
int recvSize = recv(_sock, buf, sizeof(buf) - 1, 0);
if (recvSize < 0) {
perror("recv");
}
else if (recvSize == 0) {
//如果recvSize等于0,表示对端将连接关闭了
printf("connect error\n");
return false;
}
buffer.assign(buf, recvSize);
return true;
}
void Close()
{
close(_sock);
_sock = -1;
}
private:
int _sock;
};
服务端(.cpp)
#include "tcpsvr.hpp"
int main(int argc, char* argv[])
{
if (argc != 3) {
printf("./ser [ip] [port]");
return 0;
}
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSvr ts;
if (!ts.CreateSock()) {
return 0;
}
if (!ts.Bind(ip, port)) {
return 0;
}
if (!ts.Listen(5)) {
return 0;
}
//while (1) {
TcpSvr peerts;
if (!ts.Accept(peerts)) {
return 0;
}
while (1){
std::string buf;
peerts.Recv(buf);
printf("cli say: %s\n", buf.c_str());
printf("svr say: ");
fflush(stdout);
std::cin >> buf;
peerts.Send(buf);
}
peerts.Close();
return 0;
}
客户端
#include "tcpsvr.hpp"
int main(int argc, char* argv[])
{
if (argc != 3){
printf ("./cli [ip] [port]");
return 0;
}
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSvr ts;
if (!ts.CreateSock()) {
return 0;
}
if (!ts.Connect(ip, port)) {
return 0;
}
while (1) {
printf("cli say: ");
fflush(stdout);
std::string buf;
std::cin >> buf;
ts.Send(buf);
ts.Recv(buf);
printf ("svr say: %s\n", buf.c_str());
}
ts.Close();
return 0;
}
上面的服务端只能实现同时与单个客户端进行通信,不能与多个客户端通信,因为他是单进程单线程的。
下面在服务端添加多进程,实现与多个客户端同时通信
#include "tcpsvr.hpp"
#include <sys/wait.h>
#include <signal.h>
//回收子进程
void sigcd(int signo)
{
while (1) {
(void)signo;
int ret = waitpid(-1, NULL, WNOHANG);
if (ret == 0) {
break;
}
}
}
int main(int argc, char* argv[])
{
if (argc != 3) {
printf("./svr [ip] [port]\n");
return 0;
}
signal(SIGCHLD, sigcd);
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSvr ts;
if (!ts.CreateSock()) {
return 0;
}
if(!ts.Bind(ip, port)) {
return 0;
}
if (!ts.Listen(5)) {
return 0;
}
while (1) {
//接收连接
TcpSvr peerts;
struct sockaddr_in peeraddr;
if (!ts.Accept(peerts, &peeraddr))
{
continue;
}
printf("new connect: %s:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
//创建子进程
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
else if (pid == 0) {
//子进程服务
std::string buf;
while (1) {
peerts.Recv(buf);
printf("cli say: %s\n", buf.c_str());
printf("svr say:");
fflush(stdout);
std::cin >> buf;
peerts.Send(buf);
}
peerts.Close();
exit(1);
}
peerts.Close();
}
return 0;
}