一、socket介绍
Socket即套接字,的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原义那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。
所以简单的说,socket就是:IP地址+TCP或UDP端口号。
二、socket相关api
1、创建socket
int socket(int domain,int type,int protocol);
domain:即协议域,又称为协议族(family),决定了socket的地址类型。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称 AF_UNIX,Unix域socket)、AF_ROUTE等,一般我们使用AF_INET。
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传输协议。一般使用默认,设置为0
返回值为套接字描述符
2、绑定
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:创建好的socket描述字
addr:指向要绑定给sockfd的协议地址。ipv4和ipv6使用的结构体不同
ipv4:
struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET */ in_port_t sin_port; /* port in network byte order */ struct in_addr sin_addr; /* internet address */ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ };
ipv6
struct sockaddr_in6 { sa_family_t sin6_family; /* AF_INET6 */ in_port_t sin6_port; /* port number */ uint32_t sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */ }; struct in6_addr { unsigned char s6_addr[16]; /* IPv6 address */ };
addrlen:对应的是地址的长度
3、监听
int listen(int sockfd, int backlog);
socked:创建好的socket描述字
backlog:socket可以排队的最大连接个数,一般为5~10
4、请求连接
int accept(int sockfd,struct sockaddr* addr,socklen_t* addrlen);
socked:套接字描述字
addr:用于返回客户端的协议地址
addrlen:协议地址的长度
返回值:成功返回客户端的文件描述符,失败返回-1
5、创建连接
int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen);
socked:套接字描述字
addr:要结合的地址和端口号,即服务器的socket地址
addrlen:socket地址的长度
6、发送/接收数据
调用网络I/O进行读写操作有一下几组函数。一般使用send/recv
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
sockfd:套接字描述字
buf:发送/接收缓冲区
len:缓冲区长度
flags:调用操作方式,具体组合可点击
返回值为实际写入读出的长度
7,关闭socket
int close(int fd);
int shutdown(int sockfd,int how);
三、使用socket
服务端即客户端调用顺序如下图:
四、socket通信代码实例
服务端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 4096
int main(int argc, char** argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[4096];
int n;
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
{
printf("create socket error: %s(errno: %d)/n",strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY表示任何IP
servaddr.sin_port = htons(6000); //绑定端口6000
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
{
printf("bind socket error: %s(errno: %d)/n",strerror(errno),errno);
exit(0);
}
if( listen(listenfd, 10) == -1)
{
printf("listen socket error: %s(errno: %d)/n",strerror(errno),errno);
exit(0);
}
printf("======waiting for client's request======/n");
while(1){
if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1)
{
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
n = recv(connfd, buff, MAXLINE, 0);
buff[n] = '/0';
printf("recv msg from client: %s/n", buff);
close(connfd);
}
close(listenfd);
}
客户端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 4096
int main(int argc, char** argv)
{
int sockfd, n;
char recvline[4096], sendline[4096];
struct sockaddr_in servaddr;
if( argc != 2)
{
printf("usage: ./client <ipaddress>/n");
exit(0);
}
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("create socket error: %s(errno: %d)/n", strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6000);
if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
{
printf("inet_pton error for %s/n",argv[1]);
exit(0);
}
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
printf("connect error: %s(errno: %d)/n",strerror(errno),errno);
exit(0);
}
printf("send msg to server: /n");
fgets(sendline, 4096, stdin);
if( send(sockfd, sendline, strlen(sendline), 0) < 0)
{
printf("send msg error: %s(errno: %d)/n", strerror(errno), errno);
exit(0);
}
close(sockfd);
exit(0);
}