Linux C-网络编程--上篇

1.通用套接字

#include<linux/socket.h>
struct sockaddr{	
unsigned short sa_family;//协议族类型2字节,AF_INET(TCP/IP协议)	
char sa_data[14]; //具体协议地址14字节
}

一般并不对该通用结构体进行操作,而是使用另一个等价数据结构

2.具体赋值使用的套接字

struct sockaddr_in{	
    unsigned short sin_family;    //协议族类型2字节
    unsigned short int sin_port;  //端口号2字节	
    struct in_addr sin_addr;      //IP地址4字节
    unsigned char sin_zero[8];	  //填充字节8字节,一般赋值为0
}
struct in_addr{
    unsigned long s_addr;
}

通常在编写基于TCP/IP协议的网络程序时,使用结构体sockaddr_in来设置地址,然后通过强制类型转换成sockaddr类型

struct sockaddr_in sock;
sock.sin_family=AF_INET;
sock.sin_port=htons(PORT)//设置端口号
sock.sin_addr.s_addr=inet_addr(ADDRESS);//设置地址
memset(sock.sin_zero,0x00,sizeof(sock.sin_zero))

3.创建套接字

shell下可通过man socket查看函数原型

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
/*
返回值:
	成功返回新的套接字
	失败返回-1
参数:
domain:
 	AF_UNIX 只在本机通信的套接字
 	AF_INET 使用IPv4 TCP/IP协议
 	AF_INET6 使用IPv6 TCP/IP协议
type:
 	SOCK_STREAM TCP流套接字
 	SOCK_DGRAM UDP数据报套接字
 	SOCK_RAW 原始套接字
protocol:
 	通常设置为0表示通过前两个参数确定协议
 	使用原始套接字时,系统无法唯一确定协议,此参数指定所用协议

误码存入error
*/
//TCP套接字
int sock_fd;
sock_fd = socket(AF_INET,SOCK_STREAM,0);
//UDP套接字
sock_fd = socket(AF_INET,SOCK_DGRAM,0);

4.建立连接

1.面向连接套接字(TCP)只能调一次connect
2.UDP可以调用多次,改变与目的地址的绑定


#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *serv_addr,socklen_t addrlen);
/*
成功返回0失败返回-1;错误码存入errno
若将serv_add的sa_family设置为AF_UNSPEC可以取消绑定。
addrlen为参数serv_addr的长度。
*/
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(struct sockaddr_in));
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(PORT)
inet_aton(ADDRESS,&serv_addr.sin_addr);
memset(sock.sin_zero,0,sizeof(sock.sin_zero))
connect(sock_fd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr_in));

1.若套接字类型为SOCK_STREAM,则connect用于向服务器发送请求,服务器IP地址和PORT由参数2指定。
2.若套接字类型为SOCK_DGRAM,则connect并不是建立真正连接,只是告诉内核与该套接字进行通信的目的地址(参数2),只有该目的地址发来的数据才会被该socket接收。
3.对于SOCK_DGRAM类型的套接字,调用connect的好处是不必每次发送和接收数据都指定目的地址。

5.绑定套接字

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr,socklen_t addrlen);
/*
成功返回0失败返回-1,错误码存入error
*/
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(struct sockaddr_in));
serv_addr.sin_family=AF_INET;serv_addr.sin_port=htons(PORT)
//INADDR_ANY表示可绑定到任意网络接口,对于多网卡,则表示本服务器程序将处理来自所有网络接口上相应端口的连接请求
sock.sin_addr.s_addr=htonl(INADDR_ANY);
ifbind(sock_fd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr_in))<0{
     perror("bind error");   
     exit(1);
} 

6.监听套接字

#include<sys/socket.h>
int listen(int sock_fd,int backlog);
参数backlog表示连接请求队列最大长度。若连接满,则之后连接被服务器拒绝。
成功返回0失败返回-1

注意函数listen只是将套接字设置为监听模式,并不能接收连接请求。

7.接受连接

#include<sys/types.h>
#include<sys/socket.h>
int accept(int sock_fd,struct sockaddr* addr,socklen_t* addrlen);
//参数addr保存发起连接请求的IP和端口
//成功返回新的客户端套接字,失败返回-1

Linux 套接字默认是阻塞方式,也可设置为非阻塞模式,则队列空accept立即返回-1 errno为EAGAIN

int client_fd;
int client_len;
struct sockaddr_in client_addr;
client_len=sizeof(struct sockaddr_in);
client_fd=accept(sock_fd,(struct sockaddr*)&client_addr,&lcient_len);

8.发送和接收数据

8.1TCP数据传输

//发送数据
#include<sys/type.h>
#include<sys/socket.h>
ssize_t send(int sd,const void* msg,size_t len,int flags);
//该函数只能对处于连接状态的套接字使用。
/*
sd:套接字
msg:待发送数据
len:待发送数据长度
flags:控制选项,一般设置为0,或者以下值
	MSG_OOB:指定的套接字上发送带外数据(该条件字必须支持才可以)
	MSG_DONTROUTE:通过最直接的路径发送数据,忽略下层协议的路由设置
返回值:
	成功返回实际发送字节数,失败返回-1
	发送数据过长则出现错误error设置为EMSGSIZE
	若发送数据大于该套接字的缓冲区域剩余空间,则send一般会被阻塞
	若该套接字被设置为非阻塞方式,则立即返回-1,error为EAGAIN
*/
//接收数据
#include<sys/type.h>
#include<sys/socket.h>
ssize_t recv(int sd,void* buf,size_t len,int flags);
//该函数处理面向连接的套接字即TCP连接
/*
flag:控制选项一般为0或以下值
	MSG_OOB:请求接收带外数据
	MSG_PEEK:只查看数据而不读出
	MSG_WAITALL:只在接收缓冲区满时才返回
返回值:成功返回接收的数据字节数,错误返回-1
	若数据包太长缓冲区放不下,则剩余数据可能被丢弃(根据接收数据的套接字类型而定)
	若指定套接字无数据到达时,则recv阻塞
	可设置该套接字为非阻塞模式,立即返回-1,error为EAGAIN
	函数recv接收到数据就返回,并不会等待接收到参数len指定长度的数据才返回。
*/

8.2 UDP数据传输

#include<sys/type.h>
#include<sys/socket.h>
ssize_t sendto(int sd,const void* msg,size_t len,int flags,const struct sockaddr *to,socklen_t tolen);
//该函数不需要套接字处于连接状态,可用来发送UDP数据,因为是面向无连接的套接字,因此使用sendto需要指定数据的目的地址。
//参数与send相同的一致。
//成功返回实际发送字节数,出错返回-1

#include<sys/type.h>
#include<sys/socket.h>
ssize_t recvfrom(int sd,void* buf,size_t len,int flags,struct sockaddr *from,socklen_t *fromlen);
//参数from中将保存数据的源地址
//参数fromlen调用前保存参数from的长度,调用后保存from的实际长度
//执行成功返回实际接收数据的字节数,失败返回-1

9.关闭套接字描述符

#include<unistd.h>
int close(int fd);
成功返回0,失败返回-1
#include<sys/socket.h>
int shutdown(int s,int how);
//shutdown功能比close强大,可进行更细致的控制。
参数:how指定关闭方式
	SHUT_RD:关闭连接的读通道,不再接收数据,接收缓冲区中未被读取的数据将被丢弃,仍可发送数据
	SHUT_WR:关闭连接的写通道,进程不再发送数据,发送缓冲区中还未发送的数据将被丢弃,仍可接收数据
	SHUT_RDWR:读写通道均被关闭
成功返回0,失败返回-1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章