TCP 是面向连接的、可靠的流协议。流就是指不间断的数据结构,当应用程序采用 TCP 发送消息时,虽然可以保证发送的顺序,但还是犹如没有任何间隔的数据流发送给接收端。TCP 为提供可靠性传输,实行“顺序控制”或“重发控制”机制。此外还具备“流控制(流量控制)”、“拥塞控制”、提高网络利用率等众多功能。
下面来看看三次握手的流程图:
下面来看看四次挥手的流程图:
具体流程如下图(同时挥手):
(1)socket():创建套接字
函数原型:
int socket(int domain, int type, int protocol);
参数说明:
domain:协议族
AF_UNIX, AF_LOCAL 本地通信
AF_INET ipv4
AF_INET6 ipv6
type:套接字类型
SOCK_STREAM:流式套接字
SOCK_DGRAM:数据报套接字
protocol:协议 - 填0 自动匹配底层 ,根据type 系统默认自动帮助匹配对应协议
传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP
网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)
返回值:
成功:文件描述符
失败:-1
(2)bind():绑定IP和端口
函数原型:
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数说明:
sockfd:文件描述符
addr:通用结构体,根据socket第一个参数选择的通信方式最终确定这需要真正填充传递的结构体是那个类型。强转后传参数。
addrlen:填充的结构体的大小
返回值:
成功:0
失败:-1
通用结构体:相当于预留一个空间
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
ipv4的结构体:
struct sockaddr_in {
sa_family_t sin_family; //协议族AF_INET
in_port_t sin_port; //端口
struct in_addr sin_addr;
};
struct in_addr {
uint32_t s_addr; //IP地址
};
本地址通信结构体:
struct sockaddr_un {
sa_family_t sun_family; //AF_UNIX
char sun_path[108]; //在本地创建的套接字文件的路径及名字
};
ipv6通信结构体:
struct sockaddr_in6 {
sa_family_t sin6_family;
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
struct in6_addr {
unsigned char s6_addr[16];
};
(3)listen():监听,将主动套接字变为被动套接字
函数原型:
int listen(int sockfd, int backlog);
参数说明:
sockfd:套接字
backlog:同时响应客户端请求链接的最大个数,不能写0,不同平台可同时链接的数不同,一般写6-8个
(队列1:保存正在连接)
(队列2,连接上的客户端)
返回值:
成功:0
失败:-1
(4)accept():阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,则accept()函数返回,返回一个用于通信的套接字文件;
函数原型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数说明:
Sockfd :套接字
addr:链接客户端的ip和端口号 如果不需要关心具体是哪一个客户端,那么可以填NULL; addrlen:结构体的大小 如果不需要关心具体是哪一个客户端,那么可以填NULL;
返回值:
成功:文件描述符; //用于通信
失败:-1
(5)recv():接收数据
函数原型:
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数说明:
sockfd:acceptfd
buf:接受数据的缓冲区
len:接收数据的缓冲区大小
flags:一般填0,相当于read()函数
MSG_DONTWAIT:非阻塞
返回值:
< 0 失败出错 更新errno
==0 表示客户端退出
> 0 成功接收的字节个数
6.connect():用于连接服务器
函数原型:
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数说明:
sockfd:socket函数的返回值
addr:填充的结构体是服务器端的;
addrlen:结构体的大小
返回值 :
成功:0
失败:-1
7.send():发送数据
函数原型:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数说明:
sockfd:socket函数的返回值
buf:发送内容存放的地址
len:发送内存的长度 flags:如果填0,相当于write();
服务端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if (argc != 2){
printf("please input %s \n", argv[0]);
return -1;
}
int sockfd, acceptfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0){
perror("socket err.");
return -1;
}
struct sockaddr_in serveraddr, caddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[1]));
// serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
//自动获取ip
//serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
// serveraddr.sin_addr.s_addr=INADDR_ANY; //0.0.0.0
serveraddr.sin_addr.s_addr = inet_addr("0.0.0.0");
socklen_t len = sizeof(caddr);
if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){
perror("bind err.");
return -1;
}
if (listen(sockfd, 5) < 0){
perror("listen err.");
return -1;
}
while (1){
acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
if (acceptfd < 0){
perror("accept err.");
return -1;
}
printf("client:ip=%s port=%d\n",
inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
char buf[128];
pid_t pid = fork();
if (pid < 0){
perror("fork err.");
return -1;
}
else if (pid == 0){ //发送
int sendbyte;
while (1){
fgets(buf, sizeof(buf), stdin);
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = '\0';
sendbyte = send(acceptfd, buf, sizeof(buf), 0);
if (sendbyte < 0){
perror("send error.");
return -1;
}
}
}
else{
int recvbyte;
while (1){
recvbyte = recv(acceptfd, buf, sizeof(buf), 0);
if (recvbyte < 0){
perror("recv err.");
return -1;
}
else if (recvbyte == 0){
printf("client exit.\n");
kill(pid,SIGKILL);
wait(NULL);
break;
}
else{
printf("buf:%s\n", buf);
}
}
close(acceptfd);
}
}
close(sockfd);
return 0;
}
客户端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if (argc != 3){
printf("please input %s \n", argv[0]);
return -1;
}
//1.创建套接子
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0){
perror("socket error.");
return -1;
}
printf("sockfd=%d\n", sockfd);
//填充ipv4的通信结构体 服务器的ip和端口
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
//2.请求链接服务器
if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){
perror("connect error.");
return -1;
}
//5.循环发送消息 通信
char buf[128];
pid_t pid = fork();
if (pid < 0){
perror("fork err.");
return -1;
}
else if (pid == 0){
int recvbyte;
while (1){
recvbyte = recv(sockfd, buf, sizeof(buf), 0);
if (recvbyte < 0){
perror("recv err.");
return -1;
}
printf("buf:%s\n", buf);
}
}
else{
int sendbyte;
while (1){
fgets(buf, sizeof(buf), stdin);
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = '\0';
sendbyte = send(sockfd, buf, sizeof(buf), 0);
if (sendbyte < 0){
perror("send error.");
return -1;
}
if(strncmp(buf,"quit",4)==0){
kill(pid,SIGKILL);
wait(NULL);
exit(-1);
}
}
}
close(sockfd);
return 0;
}