socket
socket类型
流式套接字(SOCK_STREAM) TCP
提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复、无丢失、无失序的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。
数据报套接字(SOCK_DGRAM) UDP
提供无连接服务、不可靠。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。
原始套接字(SOCK_RAW)
可以对较低层次协议如IP、ICMP直接访问。
函数接口
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
int socket(int domain, int type, int protocol);
作用:创建一个通信的节点(创建一个socket文件描述符)
参数:
domain:
AF_UNIX, AF_LOCAL 本地通信(进程的第七种通信) unix(7)
AF_INET 借助ipv4进行通信 ip(7)
AF_INET6 借助ipv6进行通信 ipv6(7)
type:
SOCK_STREAM:用TCP进行通信
SOCK_DGRAM:使用UDP进行通信
protocol: 0
返回值:
成功返回一个socket文件描述符
失败的话返回-1
TCP的编程流程
connect
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
作用:客户端的socket连接服务器
参数:
sockfd:socket接口返回的文件描述符
addr:服务器的地址,帮助文档的接收在bind里,
protocol: 0
返回值:
第二个参数struct sockaddr *addr的解释
struct sockaddr {
sa_family_t sa_family; //同socket接口的domain参数
char sa_data[14];
}
上面的接口类型只是socket接口族为了兼容多种协议,定义的一个通用的结构体,实际编程的时候,
需要你根据具体的协议类型,使用具体协议的结构体,对于ipv4来讲,需要看ip的第7个手册(man 7 ip)
就能得到下面这个地址
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* 端口:网络字节序 */
struct in_addr sin_addr; /* IP地址:网络字节序 */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* ip地址:网络字节序 */
};
addrlen:
地址的长度
返回值:0 -1
recv/send
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
作用:接收网络的数据
参数:
sockfd:文件描述符
buf:数据的存放缓冲区
len:buf缓冲区的最大长度
flags:默认填0 阻塞接收
返回值:
成功会返回实际接收到的字节个数
失败返回-1
如果返回0的话,代表对端退出
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
作用:发送网络的数据
参数:
sockfd:文件描述符
buf:数据的发送缓冲区
len:发的缓冲区大小
flags:默认填0
返回值:
成功会返回实际发送成功的字节个数
失败返回-1
bind
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
作用:绑定服务器地址(只允许绑定本机器的网卡地址)
参数:
sockfd:描述符
addr:代表的是本机的IP地址和端口
addrlen:地址的长度
返回值:0 -1
listen
int listen(int sockfd, int backlog);
作用:监听socket连接
参数:
sockfd:描述符
backlog:同时能处理的客户端的个数,随便赋值 5 10 15
返回值:
0 -1
accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
作用:接收一个客户端的连接,它是一个阻塞接口,直到有客户端连入的时候,会退出阻塞
参数:
sockfd:服务器的描述符
addr:入参(你传入,接口给你赋值)
addrlen:地址的长度
返回值:
错误-1
成功的话,会返回一个新的描述符,这个描述符代表的是客户端的一条链路
实例代码
客户端:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#define N 64
int main(int argc, char *argv[])
{
if(argc < 3)
{
printf("usage:%s <ip> <port>\n", argv[0]);
return -1;
}
// 0定义变量
int sockfd;
char buf[N];
int addrlen = sizeof(struct sockaddr);
struct sockaddr_in serveraddr;
// 1创建一个套接字--socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0){
perror("socket err");
exit(-1);
}
// 2指定服务器地址--sockaddr_in
bzero(&serveraddr, addrlen);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
// 3连接服务器--connect
if(connect(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0){
perror("connect err");
exit(-1);
}
// 4收发数据--recv/send
while (1) {
gets(buf);
if(strcmp(buf, "quit") == 0){
break;
}
send(sockfd, buf, N, 0);
//接收服务器的消息
bzero(buf, N);
int len = recv(sockfd, buf, N, 0);
if(len < 0)
{
perror("recv err");
break;
}
else if(len == 0)
{
printf("server exit\n");
break;
}
else
{
printf("recv server = %s\n", buf);
}
}
// 5关闭连接--close
close(sockfd);
}
服务器
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <netinet/ip.h> /* superset of previous */
#include <string.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//创建套接字
int serverfd = socket(AF_INET, SOCK_STREAM, 0);
if(serverfd < 0)
{
perror("socket err");
return -1;
}
//绑定自己的地址
struct sockaddr_in myaddr;
socklen_t addrlen = sizeof(myaddr);
memset(&myaddr, 0, addrlen);
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(8888);
#if 0
myaddr.sin_addr.s_addr = inet_addr("192.168.51.193");
#else
myaddr.sin_addr.s_addr = INADDR_ANY;
#endif
int ret = bind(serverfd, (struct sockaddr *)&myaddr, addrlen);
if(ret < 0)
{
perror("bind err");
return -1;
}
//启动监听
ret = listen(serverfd, 5);
if(ret < 0)
{
perror("bind err");
return -1;
}
//接收客户端的连接
//定义代表客户端的结构体变量
struct sockaddr_in cliaddr;
int clifd = accept(serverfd, (struct sockaddr *)&cliaddr, &addrlen);
if(clifd < 0)
{
perror("accept err");
return -1;
}
printf("新的连接过来了\n");
printf("ip = %s, port = %d\n", inet_ntoa(cliaddr.sin_addr), \
ntohs(cliaddr.sin_port));
#define N 64
char buf[N] = {0};
while (1)
{
//接收客户端的消息,如果客户端退出的话,服务器也退出
//接收服务器的消息
bzero(buf, N);
int len = recv(clifd, buf, N, 0);
if(len < 0)
{
perror("recv err");
break;
}
else if(len == 0)
{
printf("client exit\n");
break;
}
else
{
//回发给客户端
printf("recv client = %s\n", buf);
send(clifd, buf, N, 0);
}
}
close(clifd);
close(serverfd);
return 0;
}