What
Socket
也是一种IPC,允许同一计算机不同应用程序通信或者不同计算的应用程序通过网络连接进行通信。
socket
类型有两种:
-
字节流式socket
双向可靠面向连接的字节流传输,对应的就是
TCP
传输 -
数据报socket
数据报的消息形式交换,不可靠,无连接,消息到达是无序、重复或者甚至不能到达,对应
UDP
传输
相关系统调用
创建套接字
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain: AF —— Address family
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_IPX IPX - Novell protocols
AF_NETLINK Kernel user interface device netlink(7)
AF_X25 ITU-T X.25 / ISO-8208 protocol x25(7)
AF_AX25 Amateur radio AX.25 protocol
AF_ATMPVC Access to raw ATM PVCs
AF_APPLETALK AppleTalk ddp(7)
AF_PACKET Low level packet interface packet(7)
AF_ALG Interface to kernel crypto API
type: 常用SOCK_STREAM和SOCK_DGRAM
SOCK_STREAM 字节流
SOCK_DGRAM 数据报
SOCK_SEQPACKET
SOCK_RAW
SOCK_RDM
SOCK_PACKET
protocol:
一般为0即可
RETURN VALUE
` 成功 : 套接字文件描述符
失败 : -1
将Socket绑定到地址
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockefd:
socket()执行成功得到的socket文件描述符
addr:
IP地址、端口相关的结构体
addrlen:
addr的长度
RETURN VALUE
成功 : 0.
失败 : -1
struct sockaddr_in结构:
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
/* Address to accept any incoming messages. */
#define INADDR_ANY ((in_addr_t) 0x00000000)
/* Address to send to all hosts. */
#define INADDR_BROADCAST ((in_addr_t) 0xffffffff)
/* Address indicating an error return. */
#define INADDR_NONE ((in_addr_t) 0xffffffff)
连接到socket
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//一般客户端调用
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//参数的含义和bind()一致
RETURN VALUE
成功 : 0.
失败 : -1
根据socket
的连接方式分为主动连接
和被动连接
:
被动连接一般用于服务端,等待客户端的主动连接。
监听接入连接
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//用于服务端
int listen(int sockfd, int backlog);
sockfd:
socket文件描述符
backlog:
客户端可能调用connect()先于服务端调用accept(),
可能服务端忙于处理客户端,将会产生一个未决的连接
backlog用于限制未决连接的数量
RETURN VALUE
成功:0
失败:-1
接受连接
#include <sys/types.h>
#include <sys/socket.h>
//用于服务端
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
//参数含义和bind()一致
RETURN VALUE
成功:主动连接的socket文件描述符
终止连接
close()
发送数据
#include <sys/types.h>
#include <sys/socket.h>
//send()调用只能在套接字处于连接状态时使用
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
//如果在连接模式(SOCK_STREAM, SOCK_SEQPACKET)套接字上使用sendto(),
//则忽略参数dest_addr和addrlen
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd:
socket文件描述符
buf :
发送的数据
len :
发送数据的长度
flag :
一般为0即可
RETURN VALUE
成功:发送的字节数
失败:-1
send(sockfd, buf, len, flags)
和sendto(sockfd, buf, len, flags, NULL, 0)
等价
接收数据
#include <sys/types.h>
#include <sys/socket.h>
//只能用于连接状态下的socket,字节流socket,TCP
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
//一般用于数据报socket,UDP
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:
socket文件描述符
buf :
接收数据缓冲区
len :
要接收多长的数据
flag :
一般为0即可
src_addr:
发送数据者的sockaddr,调用recvfrom()的用户不想知道,可以设置为NULL
addr_len:
struct sockaddr的长度,调用recvfrom()用户不想知道,可以设置为NULL
RETURN VALUE
成功:接收的字节数
失败:-1
recv(sockfd, buf, len, flags)
等价于 recvfrom(fd, buf, len, flags, NULL, 0))
字节流socket(TCP)
服务端:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#define SERVER_PORT 9999
#define BACKLOG 10
int main(int argc, char **argv)
{
int ret;
int addr_len;
int recv_len;
int sock_server;
int sock_client;
struct sockaddr_in sock_server_addr;
struct sockaddr_in sock_client_addr;
unsigned char recv_buf[1024];
int client_num = -1;
sock_server = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sock_server)
{
perror("socket error!\n");
return -1;
}
sock_server_addr.sin_family = AF_INET;
sock_server_addr.sin_port = htons(SERVER_PORT); /* host to net, short */
sock_server_addr.sin_addr.s_addr = INADDR_ANY; //任意地址都可以链接
memset(sock_server_addr.sin_zero, 0, 8);
ret = bind(sock_server, (const struct sockaddr *)&sock_server_addr, sizeof(struct sockaddr)); //绑定IP、端口号等
if (-1 == ret)
{
perror("bind error!\n");
return -1;
}
ret = listen(sock_server, BACKLOG); //开始监听
if (-1 == ret)
{
perror("listen error!\n");
return -1;
}
while (1)
{
addr_len = sizeof(struct sockaddr);
sock_client = accept(sock_server, (struct sockaddr *)&sock_client_addr, &addr_len); //等待接受连接
if (-1 != sock_client)
{
client_num++;
printf("Get Connection from client %d : %s\n",
client_num, inet_ntoa(sock_client_addr.sin_addr));
if (!fork()) //创建子进程 来支持高并发连接
{
while (1)
{
/* 接收数据 */
recv_len = recv(sock_client, recv_buf, 1023, 0);
if (recv_len <= 0)
{
close(sock_client);
return -1; //此处返回结束的只是子进程
}
else
{
recv_buf[recv_len] = '\0';
printf("Get Message From Client %d: %s\n",
client_num, recv_buf);
}
}
}
}
}
close(sock_server);
return 0;
}
客户端:
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#define SERVER_PORT 9999
int main(int argc, char **argv)
{
int sock_client;
struct sockaddr_in sock_server_addr;
int ret;
unsigned char send_buf[1024];
int send_len;
if (argc != 2)
{
printf("Usage:\n");
printf("%s <server_ip>\n", argv[0]);
return -1;
}
sock_client = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM-创建字节流socket
sock_server_addr.sin_family = AF_INET; //IPv4
sock_server_addr.sin_port = htons(SERVER_PORT); //端口号
if (inet_aton(argv[1], &sock_server_addr.sin_addr) == 0)//从命令行参数中获得服务器IP地址
{
perror("invalid server_ip\n");
return -1;
}
memset(sock_server_addr.sin_zero, 0, 8);
ret = connect(sock_client, (const struct sockaddr *)&sock_server_addr, sizeof(struct sockaddr)); //连接服务端
if (ret == -1)
{
perror("connect error!\n");
return -1;
}
while (1)
{
if (fgets(send_buf, 1023, stdin))
{
send_len = send(sock_client, send_buf, strlen(send_buf), 0); //发送数据
if (send_len <= 0)
{
close(sock_client); //关闭socket
return -1;
}
}
}
return 0;
}
数据报socket(UDP)
服务端:
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#define SERVER_PORT 9999
int main(int argc, char **argv)
{
int ret;
int addr_len;
int recv_len;
unsigned char recv_buf[1024];
int sock_server;
int sock_client;
struct sockaddr_in sock_server_addr;
struct sockaddr_in sock_client_addr;
sock_server = socket(AF_INET, SOCK_DGRAM, 0);//SOCK_DGRAM-创建数据报socket
if (sock_server == -1)
{
perror("socket error!\n");
return -1;
}
sock_server_addr.sin_family = AF_INET;
sock_server_addr.sin_port = htons(SERVER_PORT); /* host to net, short */
sock_server_addr.sin_addr.s_addr = INADDR_ANY;
memset(sock_server_addr.sin_zero, 0, 8);
ret = bind(sock_server, (const struct sockaddr *)&sock_server_addr, sizeof(struct sockaddr));//绑定IP、端口等
if (ret == -1)
{
perror("bind error!\n");
return -1;
}
while (1)
{
addr_len = sizeof(struct sockaddr);
recv_len = recvfrom(sock_server, recv_buf, 1023, 0, (struct sockaddr *)&sock_client_addr, &addr_len); //直接接收数据,UDP不需要连接
if (recv_len > 0)
{
recv_buf[recv_len] = '\0';
printf("Get Message From %s : %s\n",
inet_ntoa(sock_client_addr.sin_addr), recv_buf);
}
}
close(sock_server);
return 0;
}
客户端:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#define SERVER_PORT 9999
int main(int argc, char **argv)
{
int ret;
unsigned char send_buf[1024];
int send_len;
int sock_client;
struct sockaddr_in sock_server_addr;
if (argc != 2)
{
printf("Usage:\n");
printf("%s <server_ip>\n", argv[0]);
return -1;
}
sock_client = socket(AF_INET, SOCK_DGRAM, 0); //SOCK_DGRAM-创建数据报socket
sock_server_addr.sin_family = AF_INET;
sock_server_addr.sin_port = htons(SERVER_PORT); /* host to net, short */
if (0 == inet_aton(argv[1], &sock_server_addr.sin_addr))
{
perror("invalid server_ip\n");
return -1;
}
memset(sock_server_addr.sin_zero, 0, 8);
ret = connect(sock_client, (const struct sockaddr *)&sock_server_addr, sizeof(struct sockaddr)); //连接服务端
if (-1 == ret)
{
perror("connect error!\n");
return -1;
}
while (1)
{
if (fgets(send_buf, 1024, stdin))
{
send_len = send(sock_client, send_buf, strlen(send_buf), 0); //发送数据
if (send_len <= 0)
{
close(sock_client);
return -1;
}
}
}
return 0;
}
IP
— 通过IP地址指定主机
Port
— 通过端口号指定应用程序(进程)