连接协议
int socket(int domain, int type, int protocol)
domain
指明所使用的的协议族,通常AF_INEF ,表示互联网协议族(TCP/IP协议族);
AF_INET IPV4 英特网域
AF_INET6 IPV6 英特网域
AF_UNIX Unix 域
AF_ROUTE 路由套接字
AF_KEY 密钥套接字
AF_UNSPEC 未指定
type 参数指定socket的类型
SOCK_STREAM:
流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性
SOCK_DGRAM
数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且保证可靠、无差错的。它使用数据协议UDP
SOCK_RAW
允许使用低层协议,原始套接字允许对低层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。
protocol
通常赋值为 0
0选择type类型对应的默认协议
IPPROTO_TCP TCP 传输协议
IPPROTO_UDP UDP 传输协议
IPPROTO_SCTP SCTP 传输协议
IPPROTO_TIPC TIPC 传输协议
地址准备好
bind() 函数: IP号端口号与相应描述字符值函数
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklent addrlen)
功能
用于绑定IP地址和端口号到socketfd
参数
sockfd
是一个socket 描述符
addr
是一个指向包含有本机IP地址及端口号等信息的sockaddr 类型的指针,指向要绑定给sockf的协议地址结构,这个地址结构根据地址创建socket 时的协议族不同而不同
// IPV4 对应
struct sockaddr
{
unisgned short as_family; // 协议族
char sa_data[4]; // IP + 端口
}
// 同等替换
struct sockaddr_in
{
sa_family_t sin_family; // 协议族
in_port_t sin_port; // 端口号
struct in_addr sin_addr; // IP地址结构体
unsigned char sin_zero[8]; // 填充 没有实际意义,只是为跟sockaddr结构体在内存中对齐,这样二者才能相互转换
}
地址转换API
// 把字符串形式的 192.168.1.123 转换为网络能识别的格式
int inet_aton(const char* straddr, struct in_addr *addrp);
// 把网络格式的IP地址转为字符串形式
char* inet_ntoa(struct in_addr inaddr);
监听
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);

连接
#include <sys/types.h>
#include <sys/socket.h>
int accept(
int sockfd,
struct sockaddr *addr,
socklen_t *addrlen
);

数据收发
字节流读取函数
在套接字通信中进行字节读取函数:read(),write()。与I/O中读取函数略有区别,因为它们输入或输出的字节数比可能请求的少
ssize_t write(int fd, const void* buf, size_t nbytes);
ssize_t read(int fd, void *buf, size_t nbyte);
// 函数均返回读或写的字节个数,出错返回-1

数据收发常用第二套API
在TCP套接字上发送数据函数:有连接
ssize_t send(int s, const void *msg, size_t len, int flags);
// 包含3个要素:套接字s, 待发数据msg, 数据长度len
// 函数只能对处于连接状态的套接字使用,参数s为已建立好连接的套接字描述
// 符,即accept函数的返回值
// 参数msg 指向存放待发送数据的缓冲区
// 参数len为待发送数据的长度,参数flags为控制选项,一般设置为0
在TCP套接字上收数据函数:有连接
ssize_t recv(int s, void *buf, size_t len, int flags);
// 包含3要素: 套接字s,接收缓冲区buf,长度len
// 函数recv从参数s所指定的套接字描述符(必须面向连接的套接字)上接收
// 数据并保存到参数buf所指定的缓存区
// 参数len则为缓冲区长度,参数flags为控制选项,一般设置为0
客户端的connect函数
connect()函数: 客户端连接主机
#include <sys/types.h>
#include <sys/socket.h>
int connect(
int sockfd,
const struct sockaddr *addr,
socklen_t addrlen
);
功能
该函数用于绑定之后的client端(客户端),与服务器建立连接
参数
sockfd
目的服务器的 sockect 描述符
addr
服务器端的IP地址和端口号的地址结构指针
addrlen
地址长度常被设置为 sizeof(struct sockaddr)
返回值
成功返回 0,遇到错误时返回 -1, 并且errno中包含相应的错误码。
字节序转换API
#include <netinet/in.h>
uint16_t htons(uint16_t host16bitvalue); //返回网络字节序的值
uint32_t htonl(uint32_t host32bitvalue); //返回网络字节序的值
uint16_t ntohs(uint16_t net16bitvalue); //返回主机字节序的值
uint32_t ntohl(uint32_t net32bitvalue); //返回主机字节序的值
h代表host,n代表net,s代表short(两个字节),l代表long(4个字节),
通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。
有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取
大致流程

实现聊天室的功能
server
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
int s_fd;
int n_read;
char readBuf[128];
int c_fd;
int mark = 0;
char msg[128] = {0};
//char *msg = "I get your connect";
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
if(argc != 3){
printf("param is not good\n");
exit(-1);
}
memset(&s_addr, 0, sizeof(struct sockaddr_in));
memset(&c_addr, 0, sizeof(struct sockaddr_in));
// 1. socket
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if(s_fd == -1){
perror("socket");
exit(-1);
}
memset(&s_addr, 0, sizeof(struct sockaddr_in));
memset(&c_addr, 0, sizeof(struct sockaddr_in));
// 1. socket
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if(s_fd == -1){
perror("socket");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &s_addr.sin_addr);
// 2. bind
bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
// 3. listen
listen(s_fd, 10);
// 4. accept
int clen = sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
if(c_fd == -1){
perror("accept");
}
mark++;
printf("get connect : %s\n", inet_ntoa(c_addr.sin_addr));
if(fork() == 0){
if(fork() == 0){
while(1){
sprintf(msg, "welcom No, %d client", mark);
write(c_fd, msg, strlen(msg));
sleep(3);
}
}
// 5. read
while(1){
memset(readBuf, 0, sizeof(readBuf));
n_read = read(c_fd, readBuf, 128);
if(n_read == -1){
perror("read");
}else{
printf("get message:%d, %s\n",n_read, readBuf);
}
}
break;
}
// 6. write
//write(c_fd, msg, strlen(msg));
}
return 0;
}
client
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
int c_fd;
int n_read;
char readBuf[128];
//char *msg = "msg from client";
char msg[128] = {0};
struct sockaddr_in c_addr;
memset(&c_addr, 0, sizeof(struct sockaddr_in));
if(argc != 3){
printf("param is not good\n");
exit(-1);
}
// 1. socket
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if(c_fd == -1){
perror("socket");
exit(-1);
}
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &c_addr.sin_addr);
// 2.connect
int con = connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr));
if(con == -1){
perror("connect");
exit(-1);
}
while(1){
if(fork() == 0){
while(1){
memset(msg, 0, sizeof(msg));
printf("input: ");
gets(msg);
write(c_fd, msg, strlen(msg));
}
}
while(1){
memset(readBuf, 0, sizeof(readBuf));
n_read = read(c_fd, readBuf, 128);
if(n_read == -1){
perror("read");
}else{
printf("get message:%d, %s\n",n_read, readBuf);
}
}
}
// 3.send
// 4.read
return 0;
}