TCP作为面向连接的,可靠的,面向字节流的协议。TCP的两端进行通信时,首先必须进行两端的连接,这篇就来详细介绍TCP的连接,在网络中的编程流程。
通讯的两端:数据提供者(服务器) 数据获取者(客户端)soket编程
在linux下,基于TCP的C/S模式,客户端和服务器之间是如何建立连接并且进行通讯的呢?这里我们就涉及到网络编程的内容,客户端一般作为为主动发起连接方也就是请求方,而服务器则是作为被动链接方也就是响应请求方。
目录
一、服务器端:被动链接方 IP地址+端口号
二、客户端:主动连接方,IP+port
三、示例代码
(1)Int listenfd(单个地址,只记录服务器) = socket(int domain,int type,int protocol);//返回文件描述符,打开网络,给外部提供连接 接收连接的套接字
Domain: 协议族AF_INET IPv4协议
Type:选择的协议tcp:SOCK_STREAM 流式服务
Udp: SOCK_DGRAM 数据报服务
Protocol:type底下的更具体地协议 通常给0
(2)Int Bind(int socketfd,const struct sockaddr *addr,socklen_t addrlen);//将服务器的IP地址+端口号与listenfd绑定;设置
Struct sockaddr_in
{
Int sin_family;//地址簇 IPV4 PF_INET == AF_INET
Short sin_port;//端口号整型值 系统预留:0-1024 预留:1025-5000 5000以上
Struct in_addr sin_addr;//IP地址
}
Struct in_addr
{
U_int32_t s_addr;//32位无符号
}
端口号转换:htons(short port);
地址转化函数:int_addr_t inet_addr(const char* strptr);
(3)Listen(int listenfd,int size);//内核启动监听,接收客户端链接(面向连接);并不会阻塞;
真正做监听工作的是内核;
Size:监听大小
早期为监听队列的大小;
现在是维护了两个队列:已完成链接的队列;size+1
正在完成链接的队列;
Listenfd:套接字
(4)Int c (地址对,具体与服务器连接上的客户端)= accept(int listenfd,struct sockaddr* cliaddr(客户端),int *len);//获取一个链接完成的客户端;提供服务
C:返回维护本次连接的文件描述符;
(5)服务于c:
接收数据:recv(int c, void* buff, int buffsize, int flag);//4个参数
发送数据:send(int c,void* buff, int datasize, int flag);//4个参数
断开连接Close(c);//关闭客户端
(1)Int sockfd = socket(int domain,int type,int protocol);
(2)Int Bind(int socketfd,const struct sockaddr *addr,socklen_t addrlen);//可选 如果没有绑定,内核也会自动选择一个可用的端口号,自动探测本地的ip地址
Struct sockaddr_in
{
Int sin_family;//地址簇 IPV4 PF_INET == AF_INET
Short sin_port;//端口号整型值 系统预留:0-1024 预留:1025-5000 5000以上
Struct in_addr sin_addr;//IP地址
}
Struct in_addr
{
U_int32_t s_addr;//32位无符号
}
端口号转换:htons(short port);
地址转化函数:int_addr_t inet_addr(const char* strptr);
(3)Connet (int sockfd,struct sockaddr * serv_addr,int addrlen);//发起连接,与服务器完成链接
(4)recv(int c, void* buff, int buffsize, int flag);//4个参数
send(int c,void* buff, int datasize, int flag);//4个参数
Close(c);
下面的示例代码主要是进行服务器和客户端的连接,在连接成功后,两端进行简单的数据交互。
//服务器端
#include
#include
#include
#include
#include
#include
#include
#include
#include
int listenfd = -1;
void sigfun(int sign)
{
close(listenfd);
exit(0);
}
int main()
{
signal(SIGINT,sigfun);
// 协议族 TCP协议
int listenfd = socket(AF_INET,SOCK_STREAM,0);
assert(listenfd != -1);
struct sockaddr_in ser,cli;
memset(&ser,0,sizeof(ser));
ser.sin_family = AF_INET;//地址簇
ser.sin_addr.s_addr = inet_addr("127.0.0.1");//ip地址
ser.sin_port = htons(6000);//端口号
int res = bind(listenfd,(struct sockaddr*)&ser,sizeof(ser));//绑定
assert(res != -1);//绑定失败:1.ip地址不对 2.端口号被占用或者没有权限用
listen(listenfd,5);//size = 5,内核维护的已完成链接的客户端的文件描述符个数(6)
while(1)//服务器接受不同客户端链接
{
int clilen = sizeof(cli);
// 记录客户端的地址信息
int c = accept(listenfd,(struct sockaddr *)&cli,&clilen);
assert(c != -1);
while(1)//与一个客户端交互的循环
{
char buff[128] = {0};
int n = recv(c,buff,127,0);//默认阻塞运行
//两次出错:recv()函数本身出错;c不存在即就是客户端主动关闭
if(n <= 0)
{
printf("one client unlink\n");
close(c);
break;
}
printf("%d,%s\n",c,buff);
send(c,"OK",2,0);//发送
}
}
close(listenfd);
}
//客户端
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
//协议族
assert(sockfd != -1);
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family = AF_INET;//地址族
ser.sin_port = htons(6000);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));
assert(res != -1);
while(1)
{
printf("please input:");
char buff[128] = {0};
fgets(buff,128,stdin);
if(strncmp(buff,"end",3) == 0)
{
close(sockfd);
break;
}
send(sockfd,buff,strlen(buff)-1,0);
char recvbuff[128] = {0};
int n = recv(sockfd,recvbuff,127,0);
if(n<=0)
{
close(sockfd);
break;
}
printf("clien recv data :%s\n",recvbuff);
}
}
这两端数据交互的运行结果如下:
总结:
1.tcp的编程流程,明白通信的两端
2.服务器端的编程流程
3.客户端的编程流程
4.简单的代码展示