socket网络通信
打算要开启apue这一个系列的专栏,即写即更!!
第一讲:socket网络通讯原理
前言
提示:本文讲述socket网络通信的部分,其中包括的内容有:网络socket通信的基本流程、涉及到的主要函数等等。
在本文内容学习之前需要了解的如下
一、文件描述符是什么?
文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,用于指代被打开的文件,对文件所有I/O操作相关的系统调用都需要通过文件描述符。(如果前面的太书面不好理解就看这一句:文件标识符是唯一标识一个文件,方便对文件进行操作的一个变量)
二、网络socket通讯的流程
1.服务器端的逻辑流程
Socket -> bind -> listen -> accept -> 进行正常的读写操作 -> close
“套接字API最初是作为UNIX操作系统的一部分而开发的,所以套接字API与系统的其它I/O设备集成在一起。因为程序要为因特网通信而创建一个套接字(socket)时,操作系统就返回一个小整数作为描述符(descriptor)来表示这个套接字。然后应用程序以该描述符作为传递参数,通过调用相应函数(如read、write、close等)来完成某种操作(如从套接字中读取或写入数据)”,这就类似于文件描述符的使用,只是使用的对象不一样。
1.是创建socket函数,对应于普通文件的打开操作,用于创建一个socket描述字,唯一标识一个socket。
listen_fd = socket(AF_INET,SOCK_STREAM,0);
2.当我们调用socket()函数创建一个socket标识符时,返回的socket描述字它存在于协议族(address family AF_XXX)空间中,但是没有一个具体的地址,但没有一个具体的地址,如果想要给它赋值一个地址,就必须调用bind()函数,为socket绑定一个地址。
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(LISTEN_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(listen_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
{
printf("create socket failure: %s\n",strerror(errno));
return -2;
}
- socket()函数创建的socket默认是一个主动类型的,but作为一个服务器,在调用socket()、bind()函数之后会调用listen()函数来监听这个socket,使该函数变为被动类型的,等待客户端的连接请求。
listen(listen_fd,BACKLOG);
4.Tcp在调用前面三个函数以后,就会监听指定了socket地址了。服务器就会调用accept()来接受来自客户端的请求,这个函数默认是一个阻塞函数,当有客户端来连接的时候会调用connect()函数,没有的话就会一直阻塞。
client_fd = accept(listen_fd,(struct sockaddr*)&cli_addr,&cliaddr_len);
读写的操作在此略过
最后和文件的读写一样,有创建打开的操作就有关闭的操作,服务器作为服务端一般是不关闭的,客户端于服务器端的信息“交流”完毕之后,关闭的是客户端。
close(client_fd);
2.客户端的逻辑流程
Socket -> bind -> listen -> accept -> 一般的读写操作 -> close
在此只谈论服务器端没出现的connect()函数
Tcp客户端调用socket()创建属于socket的标识符之后,就可以通过connect()函数来连接服务器。客户端使用这个函数表示发出连接请求,服务器端会通过accept()函数来接受请求。
connect(conn_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))
ps:一般在客户端不使用bind()函数,而是在connect()时由系统随机生成一个,但是也可以bind一个地址和端口,即使用特定的IP和端口来连服务器。(服务器端需要bind是因为要有一个确切的地址和端口来提供服务,客户端就不需要)
3.完整代码如下
socket_server.c
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define LISTEN_PORT 8889
#define BACKLOG 13
int main()
{
int rv = -1;
int listen_fd, client_fd = -1;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
socklen_t cliaddr_len;
char buf[1024];
listen_fd = socket(AF_INET,SOCK_STREAM,0);
if(listen_fd < 0)
{
printf("create socket failure: %s\n",strerror(errno));
return -1;
}
printf("socket create fd[%d]\n",listen_fd);
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(LISTEN_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(listen_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
{
printf("create socket failure: %s\n",strerror(errno));
return -2;
}
printf("socket[%d] bind on port[%d] for all IP address ok\n",listen_fd,LISTEN_PORT);
listen(listen_fd,BACKLOG);
while(1)
{
printf("\n start waiting and accept new client connect...\n");
client_fd = accept(listen_fd,(struct sockaddr*)&cli_addr,&cliaddr_len);
if(client_fd < 0)
{
printf("accept new socket failure:%s\n",strerror(errno));
return -2;
}
printf("accept new client[%s:%d] with fd [%d]\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),client_fd);
memset(buf,0,sizeof(buf));
if((rv = read(client_fd,buf,sizeof(buf))) < 0)
{
printf("read data from client socket[%d] failure: %s\n",client_fd,strerror(errno));
close(client_fd);
continue;
}
else if(rv == 0)
{
printf("client socket[%d] disconnnect\n",client_fd);
close(client_fd);
continue;
}
printf("read %d bytes data from client[%d] and echo it back: '%s'\n",rv,client_fd,buf);
if(write(client_fd,buf,rv) < 0 )
{
printf("write %d bytes data back to client[%d] failure: %s\n",rv,client_fd,strerror(errno));
close(client_fd);
}
sleep(1);
close(client_fd);
}
close(client_fd);
}
socket_client.c
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8889
#define MSG_STR "Hello, unix network program world!!"
int main()
{
int conn_fd = -1;
int rv = -1;
char buf[1024];
struct sockaddr_in serv_addr;
conn_fd = socket(AF_INET,SOCK_STREAM,0);
if(conn_fd < 0)
{
printf("create socket failure%s\n",strerror(errno));
return -1;
}
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVER_PORT);
inet_aton(SERVER_IP,&serv_addr.sin_addr);
if(connect(conn_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
{
printf("connect to server [%s:%d] failure: %s\n",SERVER_IP,SERVER_PORT,strerror(errno));
return 0;
}
if(write(conn_fd,MSG_STR,strlen(MSG_STR)) < 0)
{
printf("write data to server [%s:%d] failure: %s\n",SERVER_IP,SERVER_PORT,strerror(errno));
goto cleanup;
}
memset(buf,0,sizeof(buf));
rv = read(conn_fd,buf,sizeof(buf));
if(rv < 0)
{
printf("read data from server failure: %s\n",strerror(errno));
goto cleanup;
}
else if(0 == rv)
{
printf("client connect to server get disconnected\n");
goto cleanup;
}
printf("read %d bytes data from server:'%s'\n",rv,buf);
cleanup:
close(conn_fd);
}
总结
这就是网络socket通讯的流程,欢迎来信讨论,如有不对,感谢提醒。