一 什么是socket
socket是最初由伯克利分校为unix设计的通信机制,它对网络通信的底层(各协议栈)进行了封装(例如tcp/ip协议栈由操作系统设计,已经集成到内核中了,通过socket接口可以来调用系统通信方面的内核函数),使得底成的机制对用户来说是透明的。后来其他的厂商也实现了各自的socket,包括微软等公司,为了兼容unix套接字,他们的接口和unix socket的接口是一致的。unix socket 一般分为三种: SOCK_STREAM(tcp socket), SOCK_DGRAM(udp), raw socket(自己指定底成的协议,原生套接字).
二 unix socket使用
1. 包含的头文件
#include
#include
#include
ps:字节序分为大端自己序,和小端字节序。他们影响数据在内存中存放的规律。大端字节序表示数据的高位存在内存的低地址,数据的低位存放在高地址:如 0x12345678 ,假设内存地址从左到右依次递增,则数据从左往右的排列为: 0x12 0x34 0x56 0x78.小端字节序刚好相反。socket通信中,字节序分为网络字节序和本地字节序,网络字节序规定为大端字节序,本地字节序由自己的环境决定,既可以为大端字节序,也可以为小端字节序。在通信的过程中,如果不统一字节序,将导致数据的不一致性。
2. protocol families 协议家族
协议家族就是一组协议的集合,例如tcp/ip。AF_INET, AF_IPX, AF_PACKET,都是socket定义好的协议家族,其中AF_INET指的就是tcp/ip.以下是linux文档中指明的相关协议家族。
Name Purpose Man page
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)
3 .协议家族的地址结构
a.通用的地址结构
不同的协议家族的地址结构是不一样的,不过他们都可以转换成通用的socket地址结构:
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
b.AF_INET协议家族的地址结构
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order 2个字节*/
struct in_addr sin_addr; /* internet address 4个字节*/
char ext[8];/*应该有八个字节的填充来兼容sockaddr的,linux文档上没给出*/
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
注意:端口和地址都是网络字节序的
3.常用的接口
socket() 生成一个套接字,返回一个文件标志符,失败返回-1
int socket(int domain, int type, int protocol);
domian:协议家族
type:创建生么类型的套接字,常见的类型如下:
SOCK_STREAM :
Provides sequenced, reliable, two-way, connection-based
byte streams. An out-of-band data transmission mechanism
may be supported.
OCK_DGRAM
Supports datagrams (connectionless, unreliable messages of
a fixed maximum length)
SOCK_RAW
Provides raw network protocol access.
protocol:具体的协议,如果type不是RAW,则type就已经决定了protocol,此时可以填0,当然你以可以指明具体的协议
getsockopt(), setsockopt() - get and set options on sockets,设置和获取套接字的相关参数,失败返回-1
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
sockfd:创建的socket的文件标志符
level:级别,To manipulate(操纵) options at the sockets API level, level is specified as SOL_SOCKET. For example, to
indicate that an option is to be interpreted by the TCP protocol, level should be set to the protocol number of TCP
optname:设置参数的名称,如SO_REUSEADDR(关闭一个socket监听的服务器时,该进程将处于TIME_WAIT状态,如果设置了这个选项就可以在TIME_WAIT还没结束时,重新的开启服务器,并不是可以同时运行两个监听服务器的意思)
optval:设置的值。如 int on = 1; 把&on传进去就好了。
bind() 接口,将一个socket和一个ip绑定,失败返回-1,用于服务器端,客户端无需绑定
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
listen() 将一个socket 标记为被动套接字,不能主动发送请求,只能接收请求,用户服务器端。失败-1
int listen(int sockfd, int backlog);
backlog 服务器端的监听队列的长度,当请求数超过该队列时,如还有客户端连过来,客户端可能得到ECONNREFUSED的错误。
accept(),获取请求,返回一个新的socket文件定位符,用于和连接方进行数据传输,用于服务器端,失败返回 -1
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
addr 用于输出,客户端的地址信息(地址结构体 sockaddr)(网络字节序)
addrlen 用于输出 地址长度
注意:返回的新的socket为主动套接字,对原来的套接字没有影响
connect(),客户端连接服务器 ,失败返回-1
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
addr 输入参数, 请求连接的服务器端的地址信息
read(), 从socket中赌读取数据,返回实际读取的数据的长度
ssize_t read(int fd, void *buf, size_t count);
buf输出输出缓冲区,count缓冲区的大小
write(), 往socket中写入数据,返回实际写入的数据大小
ssize_t write(int fd, const void *buf, size_t count);
buf 输入缓冲区, count缓冲区的大小,
服务器端代码示例:
=====================================================================================================#i
#include
#include
#include
#include
#include
#define handle_err(msg) \
do{perror(msg);exit(EXIT_FAILURE);} while(1)
int main(void)
{
int backlog = SOMAXCONN;
/**
* 1.create a socket fd
*/
int ser_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (ser_sockfd < 0)
{
handle_err('create socket failed!');
}
/**
* 2. bind socket to a specified addresss,in order to marke the socket as a passive socket,if a socket be a passive it can listen
*/
struct sockaddr_in ser_addr;
memset(&ser_addr, 0, sizeof(struct sockaddr_in));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(5555);
//ser_addr.sin_addr.s_addr = inet_addr('127.0.0.1');
ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(ser_sockfd, (struct sockaddr*)&ser_addr, sizeof(struct sockaddr)) < 0)
{
handle_err('bind socket failed!');
}
/**
*3. listen the socket
*/
if (listen(ser_sockfd, backlog) < 0)
{
handle_err('listen socket failed');
}
/**
* 4 accept the client require
*/
socklen_t peer_addr_len;
struct sockaddr_in peer_addr;
int conn;
memset(&peer_addr, 0, sizeof(struct sockaddr_in));
if ((conn = accept(ser_sockfd, (struct sockaddr*)&peer_addr, &peer_addr_len)) < 0)
{
handle_err('accept require failed!');
}
while(1)
{
char bufr[255] = {0};
int r = read(conn, bufr, sizeof(bufr));
fputs(bufr,stdout);
write(conn, bufr, sizeof(bufr));
}
return 0;
}
==================================================================================================
实现客户端的代码:
#include
#include
#include
#define ERR_EXIT(msg)\
do{perror(msg);}while(1)
int main(void)
{
/**
*the client implement
*/
int clientfd = socket(AF_INET, SOCK_STREAM, 0);
if (clientfd < 0)
{
ERR_EXIT('create socket failed!');
}
/**
* connect server
*/
struct sockaddr_in clientaddr;
memset(&clientaddr, 0, sizeof(struct sockaddr_in));
clientaddr.sin_family = AF_INET;
clientaddr.sin_port =htons(5555);
clientaddr.sin_addr.s_addr = INADDR_ANY;
if (connect(clientfd,(struct sockaddr*)&clientaddr, sizeof(struct sockaddr)) < 0)
{
ERR_EXIT('failed to connect to server!');
}
while(1)
{
char bufw[255] = {0};
char bufr[255] = {0};
while(fgets(bufw, sizeof(bufw), stdin))
{
write(clientfd, bufw, sizeof(bufw));
int r = read(clientfd, bufr, sizeof(bufr));
fputs(bufr,stdout);
}
}
}