(一)socket通信简介
网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“端口”可以唯一标识主机中的应用程序(进程)。这样利用二元组(ip地址,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX BSD的套接字(socket)来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是我为什么说“一切皆socket”。 TCP/IP协议族包括运输层、网络层、链路层,而socket所在位置如图,Socket是应用层与TCP/IP协议族通信的中间软件抽象层。
Socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”。在许多操作系统中,套接字API最初是作为UNIX操作
系统的一部分而开发的,所以套接字API与系统的其他I/O设备集成在一起。应用程序要为因特网通信而创建一个套接字(socket)时,操作系统就返回一个小整数作为描述符(descriptor)来标识这个套接字。然后应用程序以该描述符作为传递参数,通过调用相应函数(如read、write、close等)来完成某种操作(如从套接字中读取或写入数据)。在生活中,A要电话给B,A拨号,B听到电话铃声后提起电话,这时A和B就建立起了连接,A和B就可以讲话了。等交流结束,挂断电话结束此次交谈。 打电话很简单解释了这工作原理:“open—write/read—close”模式。下面是网络socket通信的基本流程:
(二)socket服务器代码
#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 argc, char **argv)
{
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("\nStart waiting and accept new client connect...\n", listen_fd);
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] disconnected\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(listen_fd);
}
(二)socket客户端代码
#include <stdio.h>
#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 argc, char **argv)
{
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);
}
两者实现交互通信之后的结果为:
当运行服务器端的程序的时候,程序会阻塞,客户端连接上后就会给服务器端发送一个字符串,服务器端在收到这个字符串后会将这个字符串内容回射给客户端。*