TCP(即传输控制协议):
是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)
注意
对于TCP是先运行服务器,客户端才能运行。
流程:
图片流程
代码流程
服务器:
socket:创建一个用与链接的套接字
bind:绑定自己的ip地址和端口
listen:监听,将主动套接字转为被动套接字
accept:阻塞等待客户端链接,链接成功返回一个用于通信套接字
recv:接收消息
send:发送消息
close:关闭文件描述符
客户端:
socket:创建一个套接字
填充结构体:填充服务器的ip和端口
connect:阻塞等待链接服务器
recv/send:接收/发送消息
close:关闭
函数接口
socket创建套接字
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:创建套接字
参数:
domain:协议族
AF_UNIX, AF_LOCAL 本地通信协议 unix(7)
AF_INET IPv4 ip(7)
AF_INET6 IPv6 ipv6(7)
type:套接字类型
SOCK_STREAM 流式套接字
SOCK_DGRAM 数据报套接字
SOCK_RAW 原始套接字
protocol(协议):填0,自动匹配底层TCP或UDP等协议。根据type匹配
系统默认自动帮助匹配对应协议
传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP
网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)
返回值:成功文件描述符
失败-1 更新errno
举例
int sockfe = socket(AF_INET, SOCK_STREAM, 0);//创建套接字,用于链接
if (sockfd < 0)
{
perror("socket err.");
return -1;
}
bind绑定套接字
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:绑定套接字 (绑定ip和端口)
参数:
sockfd:套接字文件描述符
addr:用于通信结构体 (提供的是通用结构体,需要根据选择通信方式,填充对应结构体-通信结构体由socket第一个参数确定)
addrlen:结构体大小
返回值: 成功0
失败:-1 更新errno
通用结构体:
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
ipv4的通信结构体:
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
struct sockaddr_in
{
sa_family_t sin_family; /*AF_INET(协议) */
in_port_t sin_port; /* 端口 */
struct in_addr sin_addr; /* ip地址 */
};
struct in_addr
{
uint32_t s_addr;
};
本地通信结构体:
struct sockaddr_un
{
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* 套接字文件 */
};
举例
//填充ipv4的通信结构体
struct sockaddr_in serveraddr, caddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[1]));
//serveraddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //0.0.0.0 自动获取主机ip
// serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_addr.s_addr = INADDR_ANY;
socklen_t len = sizeof(caddr);
//&serveraddr -->struct sockaddr_in *
//2.绑定套接字 bind (绑定自己的ip和端口,便于别人找到自己)
if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
perror("bind err.");
return -1;
}
listen监听套接字
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:监听,将主动套接字变为被动套接字
参数:
sockfd:套接字
backlog:同时响应客户端请求链接的最大个数,不能写0.
不同平台可同时链接的数不同,一般写6-8个
(队列1:保存正在连接)
(队列2,连接上的客户端)
返回值:成功 0 失败-1,更新errno
举例
//3.listen 监听 将主动套接子变为被动等待
if (listen(sockfd, 5) < 0)
{
perror("listen err.");
return -1;
}
accept阻塞函数
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,则accept()函数返回,返回一个用于通信的套接字文件; //accept(sockfd,NULL,NULL);
参数:
Sockfd :套接字
addr: 链接客户端的ip和端口号//建立新的结构体,保存客户端ip和端口
如果不需要关心具体是哪一个客户端,那么可以填NULL;
addrlen:结构体的大小
如果不需要关心具体是哪一个客户端,那么可以填NULL;
返回值:
成功:文件描述符; //用于通信
失败:-1,更新errno
举例
//4.阻塞等待客户端链接,链接成功返回一个通信文件描述符 accept
acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
if (acceptfd < 0)
{
perror("accept err.");
return -1;
}
recv接收数据
#include <sys/types.h>
#include <sys/socket.h>
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 成功接收的字节个数
举例
recvbyte = recv(acceptfd, buf, sizeof(buf),0);
if (recvbyte < 0)
{
perror("recv err.");
return -1;
}
else if (recvbyte == 0)
{
printf("client exit.\n");
break;
}
else
{
printf("buf:%s\n", buf);
} //注意最后关闭套接字close(acceptfd);
send发送数据
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:发送数据
参数:
sockfd:socket函数的返回值
buf:发送内容存放的地址
len:发送内存的长度
flags:如果填0,相当于write();
返回值:
< 0 失败出错 更新errno
>0 成功发送的字节个数
举例
char buf[128]={0};
send(sockfd,buf,sizeof(buf),0);
connect连接服务器
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:用于连接服务器;
参数:
sockfd:socket函数的返回值
addr:填充的结构体是服务器端的;
addrlen:结构体的大小
返回值
-1 失败,更新errno
正确 0
举例
//填充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]);
//&serveraddr -->struct sockaddr_in *
//2.请求链接 connect
if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
perror("connect err.");
return -1;
}
printf("connect ok.\n");
总代码
服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
if(argc != 3)
{
printf("please input %s <ip> <port>\n",argv[0]);
return -1;
}
//1.创建套接字 socket
int sockfd,acceptfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket err.");
return -1;
}
printf("socket ok %d\n", sockfd);
//填充ipv4的通信结构体
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]);
//&serveraddr -->struct sockaddr_in *
//2.绑定套接字 bind (绑定自己的ip和端口,便于别人找到自己)
if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
perror("bind err.");
return -1;
}
printf("bind ok.\n");
//3.listen 监听 将主动套接子变为被动等待
if (listen(sockfd, 5) < 0)
{
perror("listen err.");
return -1;
}
printf("listen ok.\n");
//4.阻塞等待客户端链接,链接成功返回一个通信文件描述符 accept
acceptfd=accept(sockfd,NULL,NULL);
if(acceptfd < 0)
{
perror("accept err.");
return -1;
}
printf("accept ok.\n");
//5.循环收发消息
char buf[128];
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");
break;
}else
{
printf("buf:%s\n",buf);
}
}
close(acceptfd);
close(sockfd);
return 0;
}
客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
if(argc != 3)
{
printf("please input %s <ip> <port>\n",argv[0]);
return -1;
}
//1.创建套接字 socket
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket err.");
return -1;
}
printf("socket ok %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]);
//&serveraddr -->struct sockaddr_in *
//2.请求链接 connect
if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
perror("connect err.");
return -1;
}
printf("connect ok.\n");
//3.循环收发消息
char buf[128];
int recvbyte;
while(1)
{
fgets(buf,sizeof(buf),stdin);
if(buf[strlen(buf)-1]=='\n')
buf[strlen(buf)-1]='\0';
//发送消息
send(sockfd,buf,sizeof(buf),0);
}
close(sockfd);
return 0;
}