TCP即传输控制协议,是一种面向连接的、可靠的、基于字节流的通信协议。每发出一个数据包都要求确认,如果有一个数据包丢失,就收不到确认,发送方就必须重发这个数据包。为了保证传输的可靠性,TCP协议在UDP基础之上建立了三次对话的确认机制,即在正式收发数据前,必须和对方建立可靠的连接。TCP数据包由首部和数据两部分组成,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的长度,以确保单个TCP数据包不必再分割。
传输层的主要工作是定义端口,标识应用程序身份,实现端口到端口的通信,TCP协议可以保证数据传输的可靠性.
socket的使用场景以及分类
socket本来也是用于本地进程间通信的,后来有了TCP/IP协议簇的加入,才能实现跨主机通信。
socket是一个函数,我们可以指定参数告诉内核封装什么样的协议。socket是一种特殊的文件描述符 (everything in Unix is a file)并不仅限于TCP/IP协议,其他体系结构也会用到 socket。
套接字分为:
流式套接字(SOCK_STREAM):
TCP提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。
数据报套接字(SOCK_DGRAM)UDP:
提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。
原始套接字(SOCK_RAW)
可以对较低层次协议如IP、ICMP直接访问
#include
int socket(int domain, int type, int proto-col);
domain :
通信方式,协议簇:
Name Purpose Man page
AF_UNIX Local communication unix(7)
AF_LOCAL Synonym for AF_UNIX
AF_INET IPv4 Internet protocols ip(7)
AF_AX25 Amateur radio AX.25 protocol ax25(4)
AF_IPX IPX - Novell protocols
AF_APPLETALK AppleTalk ddp(7)
AF_X25 ITU-T X.25 / ISO-8208 protocol x25(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_DECnet DECet protocol sockets
AF_KEY Key management protocol, original
type:
传输模型
tcp:SOCK_STREAM
udp:SOCK_DGRAM
proto-col:
额外协议
ipv4协议 为0
#include
uint16_t htons(uint16_t hostshort);
功能:
主机字节序转换成网络字节序(一般都是小端转大端)
返回值:
成功返回转换过后的数值
参数列表:
hostshort:要转换的值,只能两个字节
方式一:
int inet_aton(const char *cp, struct in_addr *inp);
功能:
把字符串格式转换成网络字节序的二进制格式
返回值:
成功返回1,失败返回0
参数列表:
cp:字符串
inp:存放转换后的数值
方式二:
in_addr_t inet_addr(const char *cp);
功能:
把字符串格式转换成网络字节序的二进制格式
返回值:
返回转换成功后的二进制
参数列表:
cp:字符串
方式三:
IP地址直接设置为INADDR_ANY
INADDR_ANY是一个常量,它指代的是一个特殊的IP地址,即0.0.0.0。在网络编程中,当一个进程需要绑定一个网络端口时,可以使用INADDR_ANY来指定该端口可以接受来自任何IP地址的连接请求。
具体来说,当一个进程需要监听某个网络端口时,需要调用bind()函数将该端口与一个IP地址绑定。如果使用INADDR_ANY作为IP地址参数,就表示该端口可以接受来自任何IP地址的连接请求。这样,无论是本地主机还是远程主机,只要它们能够访问该端口,就可以与该进程建立连接。
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
int sockfd:
套接字
onst struct sockaddr *addr:
ip地址和端口信息(里面存放字符串和整数,所以需要类型转换) man 7 ip查看
struct sockaddr {
//协议簇
sa_family_t sa_family;
char sa_data[14];
}
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
* Internet address. */
struct in_addr {
/* address in network byte order */
uint32_t s_addr;
};
ip地址端口信息
socklen_t addrlen:
结构体大小
int listen(int sockfd, int backlog);
功能:
规定监听队列的大小
返回值:
成功返回0,失败返回-1
参数:
int backlog:
规定大小,队列大小为2*backlog+1
从已决队列中拿出一个完成三次握手的连接请求
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:
从已决队列中拿出一个完成三次握手的连接请求,没有请求就阻塞等待
返回值:
成功返回一个新的用于通信的套接字,失败返回-1
参数列表:
sockfd:基础套接字
addr:用于存放客户端信息
addrlen:结构体大小,不能&sizeof()
int inet_aton(const char *cp, struct in_addr *inp);
功能:
把ip地址转化为用于网络传输的二进制数值
返回值:
地址
char *ip=inet_ntoa(clt.sin_addr);
int port=ntohs(clt.sin_port);
//收消息
1. ssize_t recv(int sockfd, void *buf, size_t len, int flags);
int sockfd
该参数指明从文件描述符fd的缓冲区读取数据。
void *buf
recv()函数簇读到的数据将保存到buf所指向的空间中,用法和read()同理。
size_t len
该参数指明本次调用接收数据的最大长度。
int flags
如果flags设置为0,那么recv()函数和read()没有任何区别。flags还可以通过OR设置如下参数:
/*===============================================
* 文件名称:tcpSever.c
* 创 建 者:
* 创建日期:2024年01月02日
* 描 述:
================================================*/
#include
#include
#include
#include
#include
#include
#include
#define SEVER_PORT 8080
#define SEVER_IP "192.168.2.138"
int main(int argc, char *argv[])
{
//1.创建套接字(ipv4协议)
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd<0){
perror("sfd");
return-1;
}
printf("创建成功\n");
printf("%d\n",sfd);
//解决端口占用
int optval=1;
setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval));
int ret=0;
char buf[1024];
//2.绑定套接字
struct sockaddr_in ser;
ser.sin_family=AF_INET;
//格式化端口
ser.sin_port=htons(SEVER_PORT);
//格式化ip
ser.sin_addr.s_addr=inet_addr(SEVER_IP);
//3.绑定socket
ret=bind(sfd,(struct sockaddr*)&ser,sizeof(ser));
if(ret<0){
perror("bind");
return-1;
}
printf("绑定成功\n");
//4.监听套接字
ret=listen(sfd,5);
if(ret<0){
perror("listen");
return-1;
}
printf("监听成功\n");
printf("%s %hu\n",SEVER_IP,SEVER_PORT);
//5.阻塞连接
struct sockaddr_in clt;
int len=sizeof(struct sockaddr_in);
//6.返回客户端描述符套接字
int cfd=accept(sfd,(struct sockaddr *)&clt,&len);
printf("%d\n",cfd);
//7.拿到客户端的IP和端口号
char *ip=inet_ntoa(clt.sin_addr);
int port=ntohs(clt.sin_port);
printf("客户端ip: %s 端口号: %d\n",ip,port);
//8.接收数据
while(1){
//清空缓冲区
bzero(buf,sizeof(buf));
//收消息
ret=recv(cfd,buf,sizeof(buf),0);
if(ret==0){
perror("recv");
return-1;
}
printf("收到数据: %s 大小为: %d\n",buf,ret);
//主动退出
if(!strcmp(buf,"exit")){
break;
}
}
//9.关闭套接字
close(sfd);
close(cfd);
return 0;
}
源码
/*===============================================
* 文件名称:tcpClient.c
* 创 建 者:
* 创建日期:2024年01月02日
* 描 述:
================================================*/
#include
#include
#include
#include
#include
#include
#include
#define SEVER_PORT 8080
#define SEVER_IP "192.168.2.138"
int main(int argc, char **argv)
{
//1.创建套接字(ipv4协议)
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd<0){
perror("sfd");
return-1;
}
printf("创建成功\n");
printf("%d\n",sfd);
int ret=0;
char buf[1024]={0};
struct sockaddr_in ser;
ser.sin_family=AF_INET;
//格式化端口
ser.sin_port=htons(SEVER_PORT);
ser.sin_addr.s_addr=inet_addr(SEVER_IP);
//3.创建连接
ret=connect(sfd,(struct sockaddr*)&ser,sizeof(ser));
if(ret<0){
perror("connect");
return-1;
}
printf("连接成功\n");
printf("%s %hu\n",SEVER_IP,SEVER_PORT);
//4.发送数据
while(1){
//清空缓冲区
bzero(buf,sizeof(buf));
//发送消息
scanf("%s",buf);
//发消息
ret=send(sfd,buf,sizeof(buf),0);
if(ret==-1){
perror("send");
return-1;
}
printf("发送数据:%d\n",ret);
//主动退出
if(!strcmp(buf,"exit")){
break;
}
}
//5.关闭套接字
close(sfd);
return 0;
}