对于基础的好的朋友可以直接取代码,如果想要看详细解析的朋友可以详看下方的解析
TCP服务端代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
/*
@brief TCP通信服务端
*/
int main()
{
//1.创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd == -1)
{
perror("sockfd failed!\n");
return 0;
}
//2.绑定端口
struct sockaddr_in serv_addr;//1.见最下方解析
memset(&serv_addr, 0, sizeof(serv_addr));//清空结构体
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//记住服务端绑定的是自己的地址,127.0.0.1就是指代的本地地址,如果是多网卡的情况,使用htonl(INADDR_ANY),表示将本地所有地址都绑定起来用于监听
serv_addr.sin_port = htons(1234);
bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));//绑定套接字与端口
//3.服务端开始监听:所谓的监听就是等待客户端的连接
listen(sockfd, 20);
//4.接受客户端的连接请求
struct sockaddr_in client_addr;//这里直接定义一个地址结构体用于存储客户端的地址信息
socklen_t c_len = sizeof(client_addr);
int connect_sockfd = accept(sockfd, (struct sockaddr*)&client_addr, &c_len);
if(connect_sockfd == -1)
{
perror("connect_sockfd failed!\n");
return 0;
}
//5.收发数据
char send_buf[32] = {0};//发送数据缓冲区
while(1)
{
scanf("%s", send_buf);
write(connect_sockfd, send_buf, sizeof(send_buf));//发送
if(strcmp(send_buf, "over") == 0)//设置发送 over 之后自动结束通信
{
write(connect_sockfd, send_buf, sizeof(send_buf));
break;
}
}
//6.关闭套接字
close(connect_sockfd);
close(sockfd);
return 0;
}
TCP客户端代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
/*
@brief TCP通信客户端
*/
int main()
{
//1.创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("sockfd failed!\n");
return 0;
}
//2.连接服务端
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//服务端地址
serv_addr.sin_port = htons(1234);
connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));//连接服务端
//3.收发数据
char recv_buf[32] = {0};
while(1)
{
memset(recv_buf, 0, sizeof(recv_buf));//清空之后再接收数据
read(sockfd, recv_buf, sizeof(recv_buf)-1);//接收数据
if(strlen(recv_buf) != 0)
{
printf("recv: %s\n", recv_buf);
}
if(strcmp(recv_buf, "over") == 0)
{
break;
}
}
//4.关闭套接字
close(sockfd);
return 0;
}
这是最基本的TCP通信协议了,对这份通信代码,能解析的点不多:
首先第一点:struct sockaddr_in serv_addr; 这个结构体是定义在<netinet/in.h>头文件中
大家可以从代码中看出 sockaddr_in 这个结构体中的成员就是ip地址簇、端口号、ip地址。
struct sockaddr_in
{
short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/
unsigned short sin_port;//端口号
struct in_addr sin_addr;//ip地址
unsigned char sin_zero[8];//没有实际意义,只是为了跟SOCKADDR结构在内存中对齐
};
不过有趣的是,这个结构体成员之一的ip地址,是用另外一个结构体定义的,以下是在linux下的对于这个结构体的定义(不同的操作系统这个定义有稍些差异),这里可以看出另定义一个结构体在linux下其实是没什么必要的,不过官方如此定义,那么在原生的时候这个定义肯定有其他的不足的地方,所以才会另外定义一个结构体来存储ip地址,大家可以不用纠结
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;//ip地址
};
其实很明显可以看出,这个结构体只有一个成员就是 s_addr,也就是ip地址,是一个无符号的32位整型,用小白看的懂的代码写出来就是 unsigned int s_addr
解析的第二点:各个函数的原型以及功能介绍
服务端:
int socket(int af, int type, int protocol);//创建一个套接字用于通信
参数解析:
af 为地址族(Address Family),也就是 ip地址类型,常用的有 AF_INET 和 AF_INET6。分别代表ipv4、ipv6
type 为数据传输方式,常用的有 SOCK_STREAM 和 SOCK_DGRAM,分别代表TCP数据流传输和UDP数据报传输
protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。我们在使用的时候既可以写明,也可以用 0 表示让系统自动推导该用什么协议(因为type已经定义了用TCP数据流方式传输)
int bind(int sock, struct sockaddr *addr, socklen_t addrlen);//绑定一个 地址+端口 作为自己的传输数据的端点
参数解析:
sock 为创建的套接字
addr 本身为前面介绍的结构体 sockaddr_in,但大家可以看出,这和定义的bind函数的参数类型不匹配啊?是的,不匹配,所以我们在用 bind( )的时候需要将 addr 从 sockaddr_in 强制转换为 sockaddr 类型。
struct sockaddr
{
sa_family_t sin_family; //地址族(Address Family),也就是地址类型
char sa_data[14]; //IP地址和端口号
};
可以看出 sockaddr 和 sockaddr_in 这两个结构体并没有什么太大的不一样,只不过 sockaddr 将 IP地址和端口 定义在了一起
addrlen 表示 定义的服务端的结构体 sockaddr_in 的大小
int listen(int sock, int backlog);//监听,等待客户端的连接
参数解析:
sock 为需要进入监听状态的套接字
backlog 为客户端请求队列的最大长度,即允许一次性有多少个客户端与服务端建立连接,这个数值一般随心,1,2,10,20等等,这与内核的缓冲区的大小有关
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);//接受来自客户端的连接,其参数定义与 bind ( ) 相同
参数解析:
sock 为创建的套接字
addr 为地址结构体 sockaddr_in,详见bind函数解析,但是这里大家要注意,这里接受来自客户端的连接,这个地址结构体不再是 服务端的地址结构体, 而是客户端的地址结构体
addrlen 表示 定义的服务端的结构体 sockaddr_in 的大小
客户端:
int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);
参数解析:
sock 为创建的套接字
addr 为服务端地址结构体 sockaddr_in,详见bind函数解析
addrlen 表示 定义的服务端的结构体 sockaddr_in 的大小
参考百度百科和C语言中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)