1、基本概念
首先还是记录一下是什么,这个是关于通信的,本地进行消息传递的方式有很多,例如管道,消息队列,共享内存等,都是比较经典的进程间通信的方式,但这都是一个设备之间的进程进行通信,要实现设备和设备之间的通信需要使用Socket来实现。
Socket将网络通信的三元组(ip地址,协议,端口)进行封装,使得进行网络中的进程间通信变得很方便。Socket的通信数据传输方式有两种比较常用的,分别是SOCK_STREAM和SOCK_DGRAM,前面一种是面向连接的数据传输方式,对数据的可靠性要求较高,类似MQTT协议中需要服务质量为1(QOS=1)的情况,第二种则是一种无连接的数据传输方式,设备只会发送数据不做数据校验,不会去管接收方有没有收到数据。(但是这样同样也提高了数据传输的效率)
2、建立连接的过程
Socket是基于TCP/IP协议的,这里的TCP/IP是指[TCP(传输控制协议)和IP(网际协议)],可以直接理解为一套网络通信的标准协议格式。对于SOCKET开发者,TCP创建过程和连接拆除过程是由TCP/IP协议栈自动创建的。因此开发者并不需要控制这个过程。
但是可以稍微有一个概念性的了解,例如下面的TCP建立连接时候的三次握手和断开连接的四次挥手:
三次挥手
理解为:
- A->B:想与B建立连接
- B->A:同意建立连接
- A->B:收到
之后就是AB之间进行数据的收发了,连接已经建立好了。
四次握手
理解为:
- A->B:想断开连接
- B->A:等待处理一下
- B->A:好了,可以断开连接
- A->B:收到
之后就是断开连接,关闭Socket不在进行互相通信了。
3、Socket常用接口
Socket常用接口可以表示为下图所示:(下面的两种分别对应上述的两种常见的Socket接口)
TCP_Socket:
UDP_Socket
4、示例程序
上述过程的示例程序可以表现为:
tcp_server.c
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#define PORT 12345 //端口号
#define BACKLOG 5 //最大监听数
int main()
{
int iSocketFD = 0; //socket句柄
int iRecvLen = 0; //接收成功后的返回值
int new_fd = 0; //建立连接后的句柄
char buf[4096] = {0}; //
struct sockaddr_in stLocalAddr = {0}; //本地地址信息结构图,下面有具体的属性赋值
struct sockaddr_in stRemoteAddr = {0}; //对方地址信息
socklen_t socklen = 0;
iSocketFD = socket(AF_INET, SOCK_STREAM, 0); //建立socket
if(0 > iSocketFD)
{
printf("创建socket失败!\n");
return 0;
}
stLocalAddr.sin_family = AF_INET; /*该属性表示接收本机或其他机器传输*/
stLocalAddr.sin_port = htons(PORT); /*端口号*/
stLocalAddr.sin_addr.s_addr=htonl("192.168.109.129"); /*IP,括号内容表示本机IP*/
//绑定地址结构体和socket
if(0 > bind(iSocketFD, (void *)&stLocalAddr, sizeof(stLocalAddr)))
{
printf("绑定失败!\n");
return 0;
}
//开启监听 ,第二个参数是最大监听数
if(0 > listen(iSocketFD, BACKLOG))
{
printf("监听失败!\n");
return 0;
}
printf("iSocketFD: %d\n", iSocketFD);
//在这里阻塞知道接收到消息,参数分别是socket句柄,接收到的地址信息以及大小
new_fd = accept(iSocketFD, (void *)&stRemoteAddr, &socklen);
if(0 > new_fd)
{
printf("接收失败!\n");
return 0;
}else{
printf("接收成功!\n");
//发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)
send(new_fd, "这是服务器接收成功后发回的信息!", sizeof("这是服务器接收成功后发回的信息!"), 0);
}
printf("new_fd: %d\n", new_fd);
iRecvLen = recv(new_fd, buf, sizeof(buf), 0);
if(0 >= iRecvLen) //对端关闭连接 返回0
{
printf("接收失败或者对端关闭连接!\n");
}else{
printf("buf: %s\n", buf);
}
close(new_fd);
close(iSocketFD);
return 0;
}
tcp_client.c
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#define PORT 12345 //目标地址端口号
#define ADDR "192.168.109.129" //目标地址IP
int main()
{
int iSocketFD = 0; // socket句柄
unsigned int iRemoteAddr = 0;
struct sockaddr_in stRemoteAddr = {0}; //对端,即目标地址信息
socklen_t socklen = 0;
char buf[4096] = {0}; //存储接收到的数据
iSocketFD = socket(AF_INET, SOCK_STREAM, 0); //建立socket
if (0 > iSocketFD)
{
printf("创建socket失败!\n");
return 0;
}
stRemoteAddr.sin_family = AF_INET;
stRemoteAddr.sin_port = htons(PORT);
inet_pton(AF_INET, ADDR, &iRemoteAddr);
stRemoteAddr.sin_addr.s_addr = iRemoteAddr;
//连接方法: 传入句柄,目标地址,和大小
if (0 > connect(iSocketFD, (void *)&stRemoteAddr, sizeof(stRemoteAddr)))
{
printf("连接失败!\n");
}
else
{
printf("连接成功!\n");
recv(iSocketFD, buf, sizeof(buf), 0); //将接收数据打入buf,参数分别是句柄,储存处,最大长度,其他信息(设为0即可)。
printf("Received:%s\n", buf);
}
close(iSocketFD); //关闭socket
return 0;
}
如果建立一个UDP连接的话:
udp_client.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
int main()
{
//创建socket对象
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
//创建网络通信对象
struct sockaddr_in addr;
addr.sin_family =AF_INET;
addr.sin_port =htons(1324);
addr.sin_addr.s_addr = inet_addr("192.168.109.129"); //IP根据server所在主机的IP设定,这里是运行在同一PC端
while(1)
{
printf("input:");
char buf[50];
char buf1 = 0;
scanf("%s",&buf);
sendto(sockfd,&buf,sizeof(buf),0,(struct sockaddr*)&addr,sizeof(addr));
socklen_t len=sizeof(addr);
recvfrom(sockfd,&buf1,sizeof(buf1),0,(struct sockaddr*)&addr,&len);
if(11 ==buf1)
{
printf(" server 成功接受\n");
}
else
{
printf("server 数据丢失\n");
}
}
close(sockfd);
}
udp_server.c
#include<stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main()
{
//创建socket对象
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//创建网络通信对象
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(1324);
addr.sin_addr.s_addr = inet_addr("192.168.109.129");
//绑定socket对象与通信链接
int ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
if (0 > ret)
{
printf("bind\n");
return -1;
}
struct sockaddr_in cli;
socklen_t len = sizeof(cli);
while (1)
{
char buf[50];
char buf1;
recvfrom(sockfd, &buf, sizeof(buf), 0, (struct sockaddr *)&cli, &len);
printf("recv num =%s\n", buf);
buf1 = 11;
sendto(sockfd, &buf1, sizeof(buf1), 0, (struct sockaddr *)&cli, len);
}
close(sockfd);
}
使用的makefile
SOURCE = $(wildcard *.c)
TARGETS = $(patsubst %.c, %, $(SOURCE))
CC = gcc
CFLAGS = -Wall -g -lm#如果有math.h要加上这个lm选项
all:$(TARGETS)
$(TARGETS):%:%.c
$(CC) $< $(CFLAGS) -o $@
.PHONY:clean all
clean:
-rm -rf $(TARGETS)
参考:
https://www.jianshu.com/p/066d99da7cbd
https://www.cnblogs.com/hissia/p/5730135.html
https://blog.csdn.net/lzl980111/article/details/106188393