深入理解C语言中的Socket编程

深入理解C语言中的Socket编程

源码:文章末尾.

1. 引言

Socket编程是网络编程的基础,通过Socket编程,我们可以实现计算机之间的通信。Socket编程广泛应用于网络服务、即时通讯等领域,是每个网络程序员必须掌握的技能之一。

2. 什么是Socket

Socket是一种网络通信的中间软件抽象层,通过它,应用程序可以向网络发出请求或者应答网络请求。Socket起源于Unix,是一种进程间通信的机制。

Socket的类型
  1. 流式Socket (SOCK_STREAM):基于TCP协议,提供面向连接、可靠的数据传输服务。
  2. 数据报Socket (SOCK_DGRAM):基于UDP协议,提供无连接、不可靠的数据传输服务。
Socket的工作原理

Socket编程基于客户端-服务器模型,服务器端需要监听特定的端口,并等待客户端的连接请求,客户端则主动发起连接。通过Socket接口,双方可以进行数据传输。

3. Socket编程的基本步骤
  1. 创建Socket:使用socket()函数创建一个Socket。
  2. 绑定Socket:使用bind()函数将Socket绑定到特定的IP地址和端口号。
  3. 监听连接:使用listen()函数将Socket设置为被动监听模式,等待客户端连接。
  4. 接受连接:使用accept()函数接受客户端的连接请求。
  5. 发送和接收数据:使用send()recv()函数进行数据传输。
  6. 关闭Socket:使用close()函数关闭Socket。
4. C语言中的Socket编程示例

下面是一个简单的C语言Socket编程示例,包括服务器端和客户端的代码。

服务器端
  1. 包含必要的头文件
#include <stdio.h> // 标准输入输出库 
#include <stdlib.h> // 标准库 
#include <string.h> // 字符串操作库 
#include <unistd.h> // Unix标准库 
#include <arpa/inet.h> // 套接字库

这些头文件包含了进行Socket编程所需的函数和定义。

  1. 定义端口号
#define PORT 12345

定义服务器监听的端口号。

  1. 主函数
int main() { start_server(); return 0; }

主函数调用start_server()函数启动服务器。

  1. 服务器启动函数
void start_server() { 
    int server_fd, new_socket; 
    struct sockaddr_in address; 
    int opt = 1; 
    int addrlen = sizeof(address);
     char *hello = "Hello from server";

声明所需的变量:

  • server_fd:服务器Socket文件描述符。
  • new_socket:接受客户端连接的Socket文件描述符。
  • address:存储地址信息的结构体。
  • opt:用于设置Socket选项。
  • addrlen:地址长度。
  • hello:发送给客户端的欢迎消息。
  1. 创建Socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { 
    perror("socket failed"); 
    exit(EXIT_FAILURE); 
}

使用socket()函数创建一个Socket,参数解释:

  • AF_INET:IPv4协议。
  • SOCK_STREAM:流式Socket,使用TCP协议。
  • 0:协议编号,通常设置为0表示默认。
  1. 设置Socket选项
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {         
    perror("setsockopt"); 
    close(server_fd); 
    exit(EXIT_FAILURE); 
}

使用setsockopt()函数设置Socket选项,允许端口重用。

  1. 绑定Socket
address.sin_family = AF_INET; 
address.sin_addr.s_addr = INADDR_ANY;
 address.sin_port = htons(PORT); 
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
    perror("bind failed"); 
    close(server_fd);
    exit(EXIT_FAILURE); 
}

将Socket绑定到指定的IP地址和端口:

  • sin_family:地址族,IPv4。
  • sin_addr.s_addr:绑定到所有可用的接口。
  • sin_port:将端口号从主机字节序转换为网络字节序。
  1. 监听连接
if (listen(server_fd, 3) < 0) { 
    perror("listen"); 
    close(server_fd); 
    exit(EXIT_FAILURE); 
}

将Socket设置为监听模式,准备接受客户端连接。

  1. 接受客户端连接
while (1) { 
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)< 0) 
{ 
    perror("accept");
    close(server_fd); 
    exit(EXIT_FAILURE);
 }

使用accept()函数接受客户端连接请求,返回一个新的Socket文件描述符new_socket用于与客户端通信。

  1. 发送数据并关闭连接
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sent\n"); 
close(new_socket); 
}

使用send()函数向客户端发送数据,并关闭连接。

客户端
  1. 包含必要的头文件
#include <stdio.h> // 标准输入输出库 
#include <stdlib.h> // 标准库 
#include <string.h> // 字符串操作库 
#include <unistd.h> // Unix标准库 
#include <arpa/inet.h> // 套接字库

这些头文件包含了进行Socket编程所需的函数和定义。

  1. 定义端口号
#define PORT 12345

定义客户端连接的服务器端口号。

  1. 主函数
int main() { 
    start_client(); 
    return 0; 
}

主函数调用start_client()函数启动客户端。

  1. 客户端启动函数
void start_client() { 
    int sock = 0, valread; 
    struct sockaddr_in serv_addr; 
    char buffer[1024] = {0};

声明所需的变量:

  • sock:客户端Socket文件描述符。
  • valread:读取数据的字节数。
  • serv_addr:存储服务器地址信息的结构体。
  • buffer:用于接收数据的缓冲区。
  1. 创建Socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 
    printf("\n Socket creation error \n"); 
    return; 
}

使用socket()函数创建一个Socket,参数解释:

  • AF_INET:IPv4协议。
  • SOCK_STREAM:流式Socket,使用TCP协议。
  • 0:协议编号,通常设置为0表示默认。
  1. 设置服务器地址
serv_addr.sin_family = AF_INET; 
serv_addr.sin_port = htons(PORT); 
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { 
    printf("\nInvalid address/ Address not supported \n"); 
    return; 
}

设置服务器的IP地址和端口号:

  • sin_family:地址族,IPv4。
  • sin_port:将端口号从主机字节序转换为网络字节序。
  • inet_pton:将IP地址从文本形式转换为二进制形式。
  1. 连接到服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {         
    printf("\nConnection Failed \n"); return; 
}

使用connect()函数连接到服务器。

  1. 读取数据并关闭连接
valread = read(sock, buffer, 1024); 
printf("%s\n", buffer); 
close(sock);

使用read()函数从服务器读取数据,并关闭Socket连接。

5. 进阶Socket编程

为了提高服务器的并发处理能力,可以使用多线程或select函数来实现并发服务器。

多线程Socket编程

使用POSIX线程库(pthread)可以创建多线程服务器,每个线程处理一个客户端连接。

使用select函数实现并发服务器

select函数可以监控多个文件描述符(包括Socket),实现I/O多路复用,从而处理多个客户端连接。

6. 实践案例
简单的聊天程序

实现一个简单的聊天室,多个客户端可以同时连接到服务器并互相发送消息。

文件传输程序

实现一个文件传输程序,客户端可以从服务器下载文件或上传文件到服务器。

7. 常见问题及解决方案
常见的Socket编程错误
  1. 端口被占用:确保端口未被其他程序占用。
  2. 网络不通:检查网络连接是否正常。
  3. 协议不匹配:确保客户端和服务器使用相同的协议和端口。
调试技巧
  1. 使用strace:跟踪系统调用,排查问题。
  2. 日志记录:在关键位置记录日志,方便调试。
8. 总结

Socket编程是实现网络通信的重要手段,掌握Socket编程有助于理解网络协议和数据传输的原理。通过实践,可以提高编程技巧和解决问题的能力。

直通车:文章socked源码

  • 14
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会倒的鸡蛋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值