套接字编程__tcp
tcp:传输控制协议:面向连接,可靠传输,面向字节流;应用场景就是数据安全性大于实时性的场景—文件传输。(不限制上层传输数据大小的传输方式)
udp网络通信程序流程:
socket接口介绍:
1.创建套接字
int socket(int domain, int type, int protocol)
domain:地址域 ---- 不同的网络地址结构 AF-INET (IPv4地址域)
type: 套接字类型 — 流式套接字/数据报套接字
流式套接字: 一种有序的,可靠的,双向的,基于连接的字节流传输 SOCK_STREAM
数据报套接字:无连接的,不可靠,有最大长度限制的传输 SOCK_DGRAM
protocol:使用协议 0----不同套接字类型下的默认协议:流式套接字tcp/数据报套接字默认是udp
IPPROTO_TCP — tcp协议 IPPROTO_UDP — udp协议
返回值:返回套接字的操作句柄-----文件描述符
2.绑定地址信息
int bind(int sockfd, struct sockaddr* addr, socklen_t len);
sockfd:创建套接字返回的操作句柄。
addr: 要绑定的地址信息结构
len:地址信息的长度
返回值:成功返回0;失败返回-1。
3.服务端开始监听
int listen(int sockfd, int backlog);
backlog:决定同一时间,服务端所能接受的客户端连接请求,但不能决定服务端能够接受多少客户端的参数。
有一个 connection pending queue 可以表示能够接受多少客户端的参数。若队列放满了,则将后续请求丢弃。
4.获取新建socket的操作句柄
int accept(int sockfd, struct sockaddr* addr, socklen_t *len);
sockfd: 监听套接字 — 指定要获取哪个pending queue中的套接字
addr : 获取一个套接字,这个套接字与指定的客户端通信,通过addr获取这个客户端信息
len:指定地址信息想要的长度以及返回实际的地址长度
返回值: 成功返回新获取的套接字的描述符; 失败返回-1;
5.接收数据
ssize_t recv(int sockfd, char *buf, int len, int flag);
sockfd:套接字操作句柄
buf: 缓冲区首地址,用于存放接收到的数据,从内核socket接收缓冲区中取出数据放入这个buf用户态缓冲区。
len: 用户想要读取的数据长度,但不能大于buf缓冲区的长度。
flag: 0 —默认阻塞操作–若缓冲区中没有数据则一直等待 MSG_DONTWAIT—非阻塞
返回值: 成功返回实际读取到的数据字节长度;连接断开返回0; 失败返回-1;
6.发送数据
ssize_t send(int sockfd, char* data, int len, int flag);
sockfd:套接字操作句柄,发送数据就是将数据拷贝到内核socket发送缓冲区中
data: 要发送的数据的首地址
int_len: 要发送的数据的长度
flag: 选项参数 默认为0—表示当前操作时阻塞操作 MSG_DONTWAIT —设置为非阻塞
如果发送数据时,socket发送缓冲区已经满了,则0默认阻塞等待;MSG_DONTWAIT就会立即报错返回。
返回值: 成功返回实际发送的的数据字节长度; 失败返回-1;若连接断开则触发异常,进程退出。
7.关闭套接字
int close(int fd);
8.客户端向服务端发送连接请求
int connect(int sockfd, int sockaddr* addr, socklen_t len);
sockfd:客户端套接字—若还未绑定地址,则操作系统会选择合适的源端地址进行绑定
addr: 服务端地址信息
len; 地址信息长度。
9.代码
1.tcpsocket.hpp
#pragma once
#include <cstdio>
#include <string>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BACKLOG 10
#define CHECK_RET(q) if((q)==false){return -1;}
class TcpSocket
{
public:
TcpSocket():_sockfd(-1){
}
//创建套接字
bool Socket() {
_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(_sockfd < 0) {
perror("socket error");
return false;
}
return true;
}
void Addr(struct sockaddr_in *addr, const std::string &ip, const uint16_t port) {
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
inet_pton(AF_INET, ip.c_str(), &(addr->sin_addr.s_addr));
}
bool Bind(const std::string &ip, const uint16_t port) {
struct sockaddr_in addr;
Addr(&addr, ip, port);
socklen_t len = sizeof(struct sockaddr_in);
int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
if(ret < 0)
{
perror("bind error");
return false;
}
return true;
}
bool Listen(int backlog = BACKLOG){
int ret = listen(_sockfd, backlog);
if(ret < 0){
perror("listen error");
return false;
}
return true;
}
bool Connect(const std::string &ip, const uint16_t port) {
struct sockaddr_in addr;
Addr(&addr, ip, port);
socklen_t len = sizeof(struct sockaddr_in);
int ret = connect(_sockfd, (struct sockaddr*)&addr, len);
if(ret < 0) {
perror("connet error");
return false;
}
return true;
}
bool Accept(TcpSocket *sock, std::string *ip = NULL, uint16_t *port = NULL){
struct sockaddr_in addr;
socklen_t len = sizeof(struct sockaddr_in);
int clisockfd = accept(_sockfd, (struct sockaddr*)&addr, &len);
if(clisockfd < 0) {
perror("accept error");
return false;
}
sock->_sockfd = clisockfd;
if(ip != NULL) {
*ip = inet_ntoa(addr.sin_addr);
}
if(port != NULL) {
*port = ntohs(addr.sin_port);
}
return true;
}
bool Send(const std::string &data) {
int ret = send(_sockfd, data.c_str(), data.size(), 0);
if(ret < 0) {
perror("send error");
return false;
}
return true;
}
bool Recv(std::string *buf) {
char tmp[4096] = {0};
int ret = recv(_sockfd, tmp, 4096, 0);
if(ret < 0) {
perror("recv error");
return false;
}else if(ret == 0){
printf("connection break\n");
return false;
}
buf->assign(tmp, ret);
return true;
}
bool Close() {
close(_sockfd);
_sockfd = -1;
}
private:
int _sockfd;
};
2.tcp_cli.cpp
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include "tcpSocket.hpp"
int main(int argc, char *argv[])
{
if(argc != 3) {
printf("em:./tcp_cli 172.17.0.1 9010 ---服务绑定的地址\n");
return -1;
}
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSocket cli_sock;
CHECK_RET(cli_sock.Socket());
CHECK_RET(cli_sock.Connect(ip, port));
while(1) {
printf("client say:");
fflush(stdout);
std::string buf;
std::cin >> buf;
CHECK_RET(cli_sock.Send(buf));
buf.clear();
CHECK_RET(cli_sock.Recv(&buf));
printf("server say: %s\n", buf.c_str());
}
cli_sock.Close();
return 0;
}
3.tcp_srv.cpp
#include<iostream>
#include<stdlib.h>
#include "tcpSocket.hpp"
int main(int argc, char *argv[])
{
if(argc != 3) {
printf("em: ./tcp_srv 172.17.0.1 9010\n");
return -1;
}
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSocket lst_sock;
CHECK_RET(lst_sock.Socket());
CHECK_RET(lst_sock.Bind(ip, port));
CHECK_RET(lst_sock.Listen());
while(1) {
TcpSocket cli_sock;
std::string cli_ip;
uint16_t cli_port;
bool ret = lst_sock.Accept(&cli_sock, &cli_ip, &cli_port);
if(ret == false) {
continue;
}
std::string buf;
if(cli_sock.Recv(&buf) == false) {
cli_sock.Close();
continue;
}
printf("client:[%s:%d] say:%s\n", &cli_ip[0], cli_port, &buf[0]);
std::cout << "server say;";
fflush(stdout);
buf.clear();
std::cin >> buf;
if(cli_sock.Send(buf) == false){
cli_sock.Close();
continue;
}
}
lst_sock.Close();
return 0;
}