基于Linux下C语言的Socket网络编程
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
Socket被广泛用作网络通信,它几乎支持所有的编程语言,各种语言对于Socket操作流程也比较类似。
服务端程序的创建流程为创建socket——绑定端口号——监听——接受连接——读和写;
客户端程序的创建流程为创建socket——通过IP和端口连接服务端——读和写。
[TOC]
服务端
1. 创建Socket
包含头文件:
#include
#include
函数原型:int socket(int domain, int type, int protocol);
socket
函数里有三个参数。
domain
选择通信协议族,常用的有以下几种。
Name | Purpose |
---|---|
AF_UNIX, AF_LOCAL | Local communication |
AF_INET(常用) | IPv4 Internet protocols |
AF_INET6 | IPv6 Internet protocols |
type
指定Socket类型,常用以下几种。
Name | Purpose |
---|---|
SOCK_STREAM | 流式套接字(TCP协议) |
SOCK_DGRAM | 数据报式套接字(UDP协议) |
protocol指定协议,常用以下几种
NAME | Purpose |
---|---|
IPPROTO_TCP | TCP传输协议 |
IPPROTO_UDP | UDP传输协议 |
IPPROTO_STCP | STCP传输协议 |
IPPROTO_TIPC | TIPC传输协议 |
type和protocol不可以随意组合,当第三个参数type
为 0
自动选择第二个参数对应的默认协议
Socket如果创建成功,则返回一个描述该网络通信端点的文件描述符,操作系统会自动分配当前最小可用的文件描述符。
示例代码
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
2. 绑定端口号和IP地址
包含头文件:
#include
#include
函数原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind()
函数把地址族中的特定地址赋给socket。
sockfd
socket描述字,通过socket创建,标识唯一一个服务端描述字。
sockaddr
结构体,通过初始化sockaddr_in
结构体然后进行强制类型转化。示例代码如下:
struct sockaddr_in *server_socket = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
memset(server_socket, 0, sizeof (struct sockaddr_in)); // 清空sockaddr_in 结构体
server_socket->sin_addr.s_addr = htonl(INADDR_ANY); // 任意IP地址
server_socket->sin_family = AF_INET; // 使用IPv4协议族
server_socket->sin_port = htons(PORT); // 端口号
addrlen
为sockaddr
的长度。
bind示例代码如下
bind(sockfd, (struct sockaddr *)server_socket, sizeof (struct sockaddr));
3. 连接和监听
包含头文件:
#include
#include
函数原型int listen(int sockfd, int backlog);
sockfd
为服务端建立socket的文件描述符
backlog
为对应socket可以排队的最大连接数
服务端调用listen()
函数监听socket
,当客户端通过connect()
函数连接服务端,发送连接请求时,listen()
就会监听到这个请求。
4. 接受客户端连接
包含头文件:
#include
#include
函数原型int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd
为服务端打开的socket描述字
addr
用于保存连接的客户端的IP地址和端口号,其内容与sockaddr结构体类似。
addrlen
是接受地址的长度。
accept()
函数如果执行成功,则返回一个由内核生成的全新的套接字,用于和新客户端之间通信。
5. 读和写
包含头文件:
#include
函数原型:ssize_t read(int fd, void *buf, size_t count);
函数原型:ssize_t write(int fd, const void *buf, size_t count);
当socket打开网络描述字后,程序可以像读写文件一样向网络描述字读或者写,对应接受和发送数据。
客户端
1. 创建socket
过程同服务端,socket返回的套接字描述符将直接用于和对端通信。
2. 连接服务器
函数原型int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd
为客户端建立socket的文件描述符
addr
为sockaddr结构体保存的服务端的IP地址和端口号,以及协议类型,例如:
struct sockaddr_in *server_addr = (struct sockaddr_in *)malloc(sizeof (struct sockaddr_in));
memset(server_addr, 0, sizeof (struct sockaddr_in));
server_addr->sin_family = AF_INET; // 使用IPv4协议族
server_addr->sin_port = htons(port); // port为服务器绑定的端口号
inet_pton(AF_INET, "xxx.xxx.xxx.xxx", &server_addr->sin_addr); // 服务端的IP地址
客户端发起tcp连接请求,服务端监听到请求并接受请求后,TCP连接建立即可以开始传输文件。(如果使用UDP协议有些许区别,暂时不做讨论。)
3.发送数据
连接建立后,客户端通过write()
和write()
函数向打开的sockfd发送或接受数据
代码示例
Server
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 8000
int main()
{
// 创建socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
// 申请服务端和客户端地址结构体空间,客户端地址用于保存新连接的地址端口信息
struct sockaddr_in *server_socket = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
struct sockaddr_in *client_socket = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
socklen_t client_address_len;
memset(server_socket, 0, sizeof (struct sockaddr_in));
memset(client_socket, 0, sizeof (struct sockaddr_in));
server_socket->sin_addr.s_addr = htonl(INADDR_ANY);
server_socket->sin_family = AF_INET;
server_socket->sin_port = htons(PORT);
// 绑定
bind(sockfd, (struct sockaddr *)server_socket, sizeof (struct sockaddr));
// 监听
listen(sockfd, 20);
// 接受
int connect_fd = accept(sockfd, (struct sockaddr *)client_socket, &client_address_len);
char buf[] = "Hello Wrold!";
// 发送
while(write(connect_fd, buf, strlen(buf)))
{
printf("send msg: %s\n", buf);
sleep(1);
}
return 0;
}
Client
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
// 创建socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
// 准备地址结构体
struct sockaddr_in *server_addr = (struct sockaddr_in *)malloc(sizeof (struct sockaddr_in));
memset(server_addr, 0, sizeof (struct sockaddr_in));
server_addr->sin_family = AF_INET;
server_addr->sin_port = htons(8000);
inet_pton(AF_INET, "127.0.0.1", &server_addr->sin_addr);
// 连接
connect(sockfd, (struct sockaddr *)server_addr, sizeof (struct sockaddr)); // sockaddr_in强制转换成sockaddr
char buf[1024] = "";
// 读取
while(read(sockfd, buf, 1024))
{
printf("recv msg: %s\n", buf);
memset(buf, 0, 1024);
}
return 0;
}