本文主要是介绍tcp/ip实现点对点通信(socket编程),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
我们日常的网络其实是由很多层来组成的,有硬件,有代码和一些约定好的通信协议组成还有我们使用的微信聊天,qq聊天等;其中分层主要有:OSI七层网络模型及TCP/IP的四层参考模型:
各层的主要作用及常用协议有:
(1)物理层:数据转换(把数字信号转为电信号)(arp,rarp)
(2)数据链路层:保证数据的正确传输(校验)
(3)网络层:寻找路由与确认主机(ip,icmp,igmp)
(4)传输层:按照规定协议来收发数据(tcp/ip,udp)
(5)会话层,表示层,应用层:这三层是自己通过socket编程,来设置客户端与主机间怎样去通信的网络编程的重点!!!
下面是关于Linux下socket编程的一些函数:
创建监听套接字函数:
//头文件
#include <sys/types.h>
#include <sys/socket.h>//函数原型
int socket(int domain, int type, int protocol);//函数介绍
/****************************************************
函数作用:创建一个套接字,用于通信(socket翻译为插座,意为在通信协议与进程间通过一个API后便能进行通信)函数形参:domain:域(选择的通信协议)AF_INET/PF_INET: 网际协议,IPv4AF_INET6/PF_INET6: 网际协议,IPv6AF_UNIX/PF_UNIX: 本地协议,可写成AF_LOCAL/PF_LOCALtype:类型SOCK_STREAM:流式套接字,对应TCPSOCK_DGRAM:数据报套接字,对应UDPprotocol:协议(一般为0),因为主要的通信协议为tcp/ip或udp函数返回值:成功: 创建成功的套接字文件描述句柄失败: -1
***************************************************/
绑定套接字函数:
//头文件
#include <sys/types.h>
#include <sys/socket.h>//函数原型
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);//函数介绍
/****************************************************
函数作用:初始化默认端口及绑定套接字和地址函数形参:sockfd:使用socket函数申请成功的套接字文件句柄addr:初始化的端口及使用的协议和IP地址等(一般不使用struct sockaddr这个结构体,会使用struct sockaddr_in这个结构体)addrlen:addr的长度函数返回值:成功: 创建成功的套接字文件描述句柄失败: -1
***************************************************/
绑定端口及IP一般只在服务器上绑定,因为服务器是需要给很多个客户端来连接的,因此客户端可以不绑定,然后系统会给客户端分配一个空闲的端口等,可以在一定程度上减轻系统压力
由于struct sockaddr这个结构体是用于存放端口及IP等信息的结构体,但由于我们给它初始化时IP和端口及使用的协议都在sa_family里面,过于麻烦于是大牛们想了一个办法:用一个内存大小和这个结构体一样的来替换它,就是struct sockaddr_in这个结构体
struct sockaddr {sa_family_t sa_family;char sa_data[14];}//===================用于替换的结构体=========================
特殊地址结构体 —— IPv4地址结构体:
struct sockaddr_in
{u_short sin_family; // 地址族u_short sin_port; // 端口struct in_addr sin_addr; // IPV4地址char sin_zero[8];
};struct in_addr
{
in_addr_t s_addr; // 无符号32位网络地址
};特殊地址结构体 —— IPv6地址结构体:
struct sockaddr_in6
{u_short sin6_family; // 地址族__be16 sin6_port; // 端口__be32 sin6_flowinfo; // 流信息
struct in6_addr sin6_addr; // IPv6地址__u32 sin6_scope_id;
};特殊地址结构体 ——UNIX域地址结构体:
struct sockaddr_un
{ u_short sun_family; // 地址族char sun_path[108]; // 套接字文件路径
};
监听连接请求函数:
//头文件
#include <sys/types.h>
#include <sys/socket.h>//函数原型
int listen(int sockfd, int backlog);//函数介绍
/****************************************************
函数作用:把sockfd套接字句柄由主动状态转为被动连接状态函数形参:sockfd:使用socket函数申请成功的套接字文件句柄backlog: 当正在处理accept时候有客户端链接, 就入队列,表示队列长度(一般设置为5即可)函数返回值:成功: 0失败: -1
***************************************************/
接受连接函数:
//头文件
#include <sys/types.h>
#include <sys/socket.h>//函数原型
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//函数介绍
/****************************************************
函数作用:接收其他客户端的连接请求,如果没有连接将会阻塞等待函数形参:sockfd:使用socket函数申请成功的套接字文件句柄addr: 用于保存当前连接的客户端的IP,端口等信息addrlen:addr这个结构体的大小(必须是一个变量的地址,不能使用sizeof(addr)!!!)函数返回值:成功: 返回当前连接的客户端的本地句柄失败: -1
***************************************************/
连接服务器函数:
//头文件
#include <sys/types.h>
#include <sys/socket.h>//函数原型
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);//函数介绍
/****************************************************
函数作用:接收其他客户端的连接请求,如果没有连接将会阻塞等待函数形参:sockfd:使用socket函数申请成功的套接字文件句柄addr: 包含被连接端的IP地址+端口号等addrlen:addr这个结构体的大小(必须是一个变量的地址,不能使用sizeof(addr)!!!)函数返回值:成功: 返回当前连接的客户端的本地句柄失败: -1
***************************************************/
发送数据函数:
常见的发送数据函数有: send() , write()、 sendto()–>常用于udp通信,这里只讲send,一般为客户端给服务器发送数据
//头文件
#include <sys/types.h>
#include <sys/socket.h>//函数原型
ssize_t send(int sockfd, const void *buf, size_t len, int flags);//函数介绍
/****************************************************
函数作用:用于发送数据函数形参:sockfd:已连接套接字buf:即将被发送的数据len:数据长度flags:发送标志。MSG_NOSIGNAL:当对端已关闭时,不产生SIGPIPE信号MSG_OOB:发送紧急(带外)数据,只针对TCP连接函数返回值:成功: 返回成功发送的字节数失败: -1
***************************************************/
接收数据函数:
常见的发送数据函数有: recv(), read(), recvfrom()–》可接收发送方的IP+端口,这里只讲recvfrom,一般为客户端给服务器发送数据
//头文件
#include <sys/types.h>
#include <sys/socket.h>//函数原型
ssize_t send(int sockfd, const void *buf, size_t len, int flags);//函数介绍
/****************************************************
函数作用:用于发送数据函数形参:sockfd:已连接套接字buf:即将被发送的数据len:数据长度flags:发送标志。MSG_NOSIGNAL:当对端已关闭时,不产生SIGPIPE信号MSG_OOB:发送紧急(带外)数据,只针对TCP连接函数返回值:成功: 返回成功发送的字节数失败: -1
***************************************************/
下面直接上代码
服务器端代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>void *client_recv(void* arg);
int main(int argc, char **argv)
{if(argc < 2){printf("Run: server port\n");exit(1);}//1.创建套接字int sockfd = socket(AF_INET,SOCK_STREAM, 0);if(sockfd < 0){perror("socket error");exit(1);}//2.绑定-地址, 端口struct sockaddr_in my_addr;memset(&my_addr, 0, sizeof(my_addr));my_addr.sin_family=AF_INET;my_addr.sin_port=htons( atoi(argv[1]) );//网络字节序my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//网络字节序--在任意ip上绑定int ret = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));if(ret < 0){perror("bind error");exit(1);}//3.监听--启动服务器ret = listen(sockfd, 5);if(ret < 0){perror("listen error");exit(1);}//4.接受连接//定义保存客户端地址的结构体struct sockaddr_in clientaddr;socklen_t len = sizeof(clientaddr);//4.接受链接--没有客户端链接是处于阻塞状态int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);if(clientfd < 0){perror("accept error");}//创建线程--用于接收客户端数据pthread_t id = 0;ret = pthread_create(&id, NULL, client_recv, (void*)&clientfd);//把与客户端通信用的套接字传递给线程if(ret <0){perror("create error");exit(1);}char buffer[128]={0};while(1){//给客户端发送数据scanf("%s", buffer);send(clientfd, buffer, strlen(buffer), 0);//清空buffermemset(buffer, 0, sizeof(buffer));}
}void *client_recv(void* arg)
{//获取客户端套接字int clientfd = *((int*)arg);char buffer[128]={0};while(1){//接收数据recv(clientfd, buffer, sizeof(buffer), 0);//输出printf("%s\n", buffer);//清空buffermemset(buffer, 0, sizeof(buffer));}}
客户端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
void *server_recv(void* arg);int main(int argc, char **argv)
{if(argc < 3){printf("Run: server ip port\n");exit(1);}//1.创建套接字int sockfd = socket(AF_INET,SOCK_STREAM, 0);if(sockfd < 0){perror("socket error");exit(1);}//2. 链接服务器struct sockaddr_in seraddr;memset(&seraddr,0, sizeof(seraddr));seraddr.sin_family = AF_INET;seraddr.sin_port = htons( atoi(argv[2]) );seraddr.sin_addr.s_addr = inet_addr( argv[1] );//服务器地址转网络字节序32位int ret = connect(sockfd, (struct sockaddr*)&seraddr, sizeof(seraddr));if(ret < 0){perror("connect error");exit(1);}//创建线程//创建线程--用于接收服务端数据pthread_t id = 0;ret = pthread_create(&id, NULL, server_recv, (void*)&sockfd);//把与客户端通信用的套接字传递给线程if(ret <0){perror("create error");exit(1);}//主线程发送数据char buffer[128]={0};while(1){//给客户端发送数据scanf("%s", buffer);send(sockfd, buffer, strlen(buffer), 0);//清空buffermemset(buffer, 0, sizeof(buffer));}
}void *server_recv(void* arg)
{//获取客户端套接字int sockfd = *((int*)arg);char buffer[128]={0};while(1){//接收数据recv(sockfd, buffer, sizeof(buffer), 0);//输出printf("%s\n", buffer);//清空buffermemset(buffer, 0, sizeof(buffer));}}
这篇关于tcp/ip实现点对点通信(socket编程)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!