socket通信简介
网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“端口”可以唯一标识主机中的应用程序(进程)。这样利用二元组(ip地址,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX BSD的套接字(socket)来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在的, TCP/IP协议族包括运输层、网络层、链路层,而socket所在位置如图,socket是应用层与TCP/IP协议族通信的中间软件抽象层。
网络socket中服务器和客户的通信基本流程如下图
socket接口函数
一、socket()函数
int socket(int domain,int type, int protocol)
1、函数说明:socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参
数,通过它来进行一些读写操作。
2、参数说明:
domain | 常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址 |
---|---|
type | 指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET |
protocol | 指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议 |
二、bind函数
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
1、函数说明:通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器。
2、函数参数:
sockfd | 即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字 |
---|---|
addrlen | 对应的是地址的长度 |
addr | 一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址 |
三、listen()函数
1、函数说明:socket()函数创建的socket默认是一个主动类型的,如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,该函数将socket变为被动类型的,等待客户的连接请求。
int listen(int sockfd, int backlog);
2、参数说明:
sockfd | socket()系统调用创建的要监听的socket描述字 |
---|---|
backlog | 相应socket可以在内核里排队的最大连接个数 |
四、accept()函数
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
1、函数说明:服务器就会调用accpet()接受来自客户端的连接请求,这个函数默认是一个阻塞函数,这也意味着如果没有客户端连接服务器的话该程序将一直阻塞着不会返回,直到有一个客户端连过来为止。一旦客户端调用connect()函数就会触发服务器的accept()返回,这时整个TCP链接就建立好了。
2、参数说明:
sockfd | 服务器开始调用socket()函数生成的,称为监听socket描述字 |
---|---|
*addr | 用于返回客户端的协议地址,这个地址里包含有客户端的IP和端口信息等 |
addrlen | 返回客户端协议地址的长度 |
五、connect函数
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
1、函数说明:客户端调用connect()发出连接请求,服务器端就会接收到这个请求并使accept()返回。
2、参数说明:
sockfd | 客户端的socket()创建的描述字 |
---|---|
addr | 要连接的服务器的socket地址信息,这里面包含有服务器的IP地址和端口等信息 |
addrlen | socket地址的长度 |
socket之服务器编程
/*********************************************************************************
* Copyright: (C) 2020 makun<[email protected]>
* All rights reserved.
*
* Filename: djc_socket_server.c
* Description: This file socekt_server
*
* Version: 1.0.0(2020年02月28日)
* Author: makun <[email protected]>
* ChangeLog: 1, Release initial version on "2020年02月28日 13时59分51秒"
*
********************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <stdlib.h>
#define MSG_STR "Hello makun iot stduio client\n"
void print_usage(char *progname)
{
printf("%s usage: \n", progname);
printf("-p(--port): sepcify server listen port.\n");
printf("-h(--Help): print this help information.\n");
return ;
}
int main (int argc, char **argv)
{
int sockfd = -1;
int clifd;
struct sockaddr_in servaddr;
struct sockaddr_in cliaddr;
socklen_t len;
int port = 0;
int ch;
int rv = -1;
int on = 1;
char buf[1024];
struct option opts[] = {
{"port", required_argument, NULL, 'p'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
while( (ch=getopt_long(argc, argv, "p:h", opts, NULL)) != -1 )
{
switch(ch)
{
case 'p':
port=atoi(optarg);
break;
case 'h':
print_usage(argv[0]);
return 0;
}
}
if( !port )
{
print_usage(argv[0]);
return 0;
}
sockfd=socket(AF_INET,SOCK_STREAM ,0);
if( sockfd < 0)
{
printf("create socket failure:%s\n",strerror(errno));
return -1;
}
printf("create socket successfuly: %d\n", sockfd);
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port = htons(port);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
rv = bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(rv < 0)
{
printf("socket[%d] bind to port[%d] failure:%s\n",sockfd,port,strerror(errno));
return -2;
}
listen(sockfd,13);
printf("start to listen on port [%d]\n", port);
while(1)
{
printf("start accept new client incoming..\n");
clifd=accept(sockfd,(struct sockaddr *)&cliaddr,&len);
if(clifd < 0)
{
printf("accept client failure:%s\n", strerror(errno));
continue;
}
printf("accept new clienr[%s:%d] successfully\n", inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
memset(buf , 0, sizeof(buf));
rv=read(clifd,buf,sizeof(buf));
if(rv < 0)
{
printf("Read data from client sockfd[%d] failure:%s\n",clifd,strerror(errno));
close(clifd);
continue;
}
else if( rv == 0)
{
printf("socket[%d]get disconnected\n", clifd);
close(clifd);
continue;
}
else if( rv > 0)
{
printf("read %d bytss data from server:%s\n",rv,buf);
}
rv=write(clifd, MSG_STR, strlen(MSG_STR));
if(rv < 0)
{
printf("Write to client by sockfd[%d] failure: %s\n", sockfd, strerror(errno));
close(clifd);
continue;
}
printf("Close client socket[%d]\n", clifd);
}
close(sockfd);
return 0;
}
linux下写完运行的结果如下:
我用windows下的TCP Test Tool 下测试的服务器代码,当我给服务器发一个“hello"的时候,服务器就回我"Hello makun iot stduio client。
socket之客户端编程
/*********************************************************************************
* Copyright: (C) 2020 makun<[email protected]>
* All rights reserved.
*
* Filename: djc_socket_server.c
* Description: This file socket_client
*
* Version: 1.0.0(2020年02月28日)
* Author: makun <[email protected]>
* ChangeLog: 1, Release initial version on "2020年02月28日 13时59分51秒"
*
********************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <stdlib.h>
#define MSG_STR "Hello makun iot stduio \n"
void print_usage(char *progname)
{
printf("%s usage: \n", progname);
printf("-p(--port): sepcify server listen port.\n");
printf("-i(--ipaddr): sepcify server ip address.\n");
printf("-h(--Help): print this help information.\n");
return ;
}
int main (int argc, char **argv)
{
int sockfd = -1;
struct sockaddr_in servaddr;
char *servip=NULL;
int port = 0;
int ch;
int rv = -1;
char buf[1024];
struct option opts[] = {
{"port", required_argument, NULL, 'p'},
{"help", no_argument, NULL, 'h'},
{"ipaddr", required_argument, NULL, 'i'},
{NULL, 0, NULL, 0}
};
while( (ch=getopt_long(argc, argv, "i:p:h", opts, NULL)) != -1 )
{
switch(ch)
{
case 'i':
servip=optarg;
break;
case 'p':
port=atoi(optarg);
break;
case 'h':
print_usage(argv[0]);
return 0;
}
}
if( !port||!servip )
{
print_usage(argv[0]);
return 0;
}
sockfd=socket(AF_INET,SOCK_STREAM ,0);
if( sockfd < 0)
{
printf("create socket failure:%s\n",strerror(errno));
return -1;
}
printf("create socket successfuly: %d\n", sockfd);
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port = htons(port);
inet_aton(servip,&servaddr.sin_addr);
rv = connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(rv < 0)
{
printf("socket[%d] bind to port[%d] failure:%s\n",sockfd,port,strerror(errno));
return -2;
}
printf("connect to server[%s:%d]successfully\n",servip, port);
while(1)
{
rv=write(sockfd, MSG_STR, strlen(MSG_STR));
if(rv < 0)
{
printf("Write to server by sockfd[%d] failure: %s\n", sockfd, strerror(errno));
break;
}
memset(buf,0,sizeof(buf));
rv=read(sockfd, buf, sizeof(buf));
if( rv < 0)
{
printf("Read data from server by sockfd[%d] failure: %s\n",
sockfd, strerror(errno));
break;
}
else if( rv == 0)
{
printf("Socket[%d] get disconnected\n", sockfd);
break;
}
else if( rv > 0 )
{
printf("Read %d bytes data from Server: %s\n",rv, buf);
continue;
}
}
close(sockfd);
}
代码运行结果:
我也是用Tcp Test Tool下测试socket下的客户端程序,当客户端连接我的Tcp Test Tool 的ip地址后,他们就建立起了通信连接,当客户端给Tcp Test Tool 服务器发送“Hello makun iot stduio”的时候,我们可以用Tcp Test Tool 服务器给客户端发一个消息"hello makun"。二者就能通信了。