常见的两种套接字类型
SOCK_STREAM TCP 流格式套接字
SOCK_DGRAM UDP 数据包套接字
基础知识:
struct sockaddr // 基本不用
{
unsigned short sa_family; // AF_INET
char sa_data[14];
};
// AF_INET是 IPv4 网络协议的套接字类型,AF_INET6 则是 IPv6 的;而 AF_UNIX 则是 Unix 系统本地通信。
struct sockaddr_in // 大小一样,可以用sockaddr *引用 sockaddr_in
{
short int sin_family; // AF_INET , 存储在本机上,不需要转变为网络字节顺序
unsigned short int sin_port; //端口号,NBO
struct in_addr sin_addr; // IP地址,NBO
unsigned char sin_zero[8]; //需要memset初始化,一般不使用
};
struct in_addr
{
unsigned long s_addr; // 存放IP地址
};
传输时要考虑字节序的问题,HBO (Host Byte Order) 和CPU有关。NBO(Network Byte Order) 是Big-endian的写法。
例子:如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为
addr big-endian little-endian // 以每个字节为单位
0x0000 0x12 0xcd
0x0001 0x23 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12
下图为socket流程图,挺好的。
实际创建socket
struct sockarrd_in server_addr; // 目的地的信息
memset(server_addr , 0 , sizeof(server_addr)); // 清空
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port );
/**
htons()--"Host to Network Short"
htonl()--"Host to Network Long"
ntohs()--"Network to Host Short"
ntohl()--"Network to Host Long"
**/
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // inet_addr( ); 直接返回IP地址的网络字节格式,错误时返回-1
// printf("%s",inet_ntoa(ina.sin_addr)); 相反的调用,返回字符串形式的IP地址,重复调用覆盖上次结果。
int socket_id = socket(AF_INET, SOCK_STEAM , 0); //socket 描述符类型为 int
/**
如果你要固定用某个端口,就用bind绑定,一般server端使用。
bind()的用法:
一旦拥有套接字,就可以把套接字和机器上的某个端口绑定起来。
bind就是这个作用。
struct sockaddr_in my_addr;
bind(socket_id,(struct sockaddr *)my_addr,sizeof(struct sockaddr));
// 其中my_addr是sockaddr_in 类型,里面的IP地址为本机地址,端口号为要绑定的端口号。
**/
建立连接
int err = connect(socket_id, ( struct sockaddr *)server_addr, sizeof(server_addr)); // connect();错误返回-1
发送数据
int send(int sock_id, char *str, int len, int flags); // 返回实际发送的字节数,出错返回-1
sock_id 是你想发送数据的套接字描述符(或者是调用 socket() 或者是accept() 返回的 )
str 是指向你想发送的数据的指针。len 是数据的长度,一般为strlen(str)。 把 flags 设置为 0 就可以了。
接受数据
int recv(int sock_id, char *buf, int len, unsigned int flags); // recv() 返回实际读入缓冲的数据的字节数。或者在错误的时候返回-1
sock_id 是要读的套接字描述符。buf 是要读的信息的缓冲。len 是buf的最大长度。flags 可以设置为0。
关闭套接字
close(sock_id);//任何在另一端读写套接字的企 图都将返回错误信息。
/**
int shutdown(int sock_id, int how);
sock_id 是你想要关闭的套接字文件描述复。how 的值是下面的其中之 一:
0 - 不允许接受
1 - 不允许发送
2 - 不允许发送和接受(和close() 一样)
shutdown() 成功时返回 0,失败时返回 -1
**/
server 端:
int listen(int sock_id, int backlog); //一般先bind绑定端口,再listen。
// sock_id 是被监听的套接字文件描述符。backlog 是在进入 队列中允许的连接数目。在发生错误的时候返回-1.
/******下面是accept(); 的使用
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
main()
{
int sockfd, new_fd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
int sin_size;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(3490);
my_addr.sin_addr.s_addr = "127.0.0.1";
bzero(&(my_addr.sin_zero); // 清空addr.sin_zero
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); //绑定端口
listen(sockfd, 10); // 监听,队列最大10
sin_size = sizeof(struct sockaddr_in); // sin_size 初始化为struct sockaddr_in 的大小
new_fd = accept(sockfd, &their_addr, &sin_size); //第一个参数为被监听的套接字,their_addr存放发送请求地的信息。返回值为新建立连接的套接字信息。
// 第三个参数为accept后存入their_addr 的字节数。
}