socket的英文翻译是接口、插座的意思,很形象,就相当于将两个台电脑用一根线连起来,线的两头分别是插头,插在两台电脑上,借此实现通信。
两台电脑通信,实际上是这两台电脑上的某个进程在进行通信。而两个进程进行通信,实际上是往同一个文件中读取数据。
在理解socket编程之前,先大概了解一下socket缓冲区的概念。
socket编程基于传输层,是应用层和传输层之间的一个抽象层。在使用socket API时,实际上每创建一个socket,都会分配两个缓冲区,输入缓冲区和输出缓冲区(大小一般是8K),Linux下一切皆文件的思想,两台主机在进行通信时,write函数是向缓冲区里写,read函数是从缓冲区里读,至于缓冲区里的数据什么时候被传输,有没有达到目标主机,这些都交给传输层的TCP/UDP来做。
但在Windows中,将socket文件和普通文件分开,所以不能用write函数和read函数实现,而是用send函数和recv函数。
每次通信都打开了一个socket文件,所以通信结束后,在进程关闭前,要关闭所有的socket文件。
socket的API是在三次握手和四次挥手的基础上设置的接口
接口中用到的结构体(如:sockaddr、sockaddr_in),初学照猫画虎即可,不用深究。
总的来说,不管是struct sockaddr还是**struct sockaddr_in **都是存放了一个ip地址,一个端口号,和ip的类型(IPV4还是IPV6)
注意:
每次输入的ip要通过inet_addr(“127.0.0.1”)函数转化,将一个点分十进制ip转换成长无符号整形,头文件在
端口号要转换成小端,具体操作及原因在网络字节序有详细介绍。
下图是三次握手的流程图:
一个客户端只有一个sock(文件描述符),而一个服务器最少有两个(一个是自己创建socket时的sock,剩下的是每有一个客户端连接服务器就生成一个sock文件描述符)。
在数据传输过程中,即相当于文件的读写操作:
四次挥手在socket API上的接口表示为关闭各自拥有的文件描述符即可。
实现了一次客户端向服务器发送数据和一次服务器向客户端响应的通信。
/*serve_tcp.c*/
#include
#include
#include
#include
#include
#include
#include
int main(){
//创建套接字
int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//初始化socket元素
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);
//绑定文件描述符和服务器的ip和端口号
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//进入监听状态,等待用户发起请求
listen(serv_sock, 20);
//接受客户端请求
//定义客户端的套接字,这里返回一个新的套接字,后面通信时,就用这个clnt_sock进行通信
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size = sizeof(clnt_addr);
int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
//接收客户端数据,并相应
char str[256];
read(clnt_sock, str, sizeof(str));
printf("client send: %s\n",str);
strcat(str, "+ACK");
write(clnt_sock, str, sizeof(str));
//关闭套接字
close(clnt_sock);
close(serv_sock);
return 0;
}
/*client_tcp.c*/
#include
#include
#include
#include
#include
#include
int main(){
//创建套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
//服务器的ip为本地,端口号1234
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(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//发送并接收数据
char buffer[40];
printf("Please write:");
scanf("%s", buffer);
write(sock, buffer, sizeof(buffer));
read(sock, buffer, sizeof(buffer) - 1);
printf("Serve send: %s\n", buffer);
//断开连接
close(sock);
return 0;
}
注意:应先运行服务器端,再运行客户端。