Linux网络编程和套接字

1、套接字概述

套接字的本意是插座,在网络中用来描述计算机中不同程序与其他计算机程序的通信方式。

常用的套接字类型有3种:

1)流套接字(SOCK——STREAM):使用了面向连接的可靠的数据通信方式,即TCP套接字

2)数据报套接字(Raw Sockets):使用了不面向连接的数据传输方式,即UDP套接字

3)原始套接字(SOCK——RAW):没有经过处理的IP数据包,可以根据自己程序的要求进行封装。

2、常用函数

1、创建套接字函数:成功时返回文件描述符,失败时返回-1

int socket(int domain,int type,int protocol);
//参数domain用于指定创建套接字所使用的协议族(可取AF_UNIX,AF_INET,AF_INTE6)
//参数type指定套接字的类型(可取SOCK_STREAM,SOCK_DGRAM,SOCK_RAW)
//参数protocol通常设置为0

2、在指定套接字上创建链接函数:成功时返回0,失败时返回-1

int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen);
//参数sockfd是一个由函数socket创建的套接字
//参数serv_addr是一个地址结构,指定服务器的IP地址和端口号
//参数addrlen为参数serv_addr的长度

3、将一个套接字和某个端口绑定在一起的函数:成功时返回0,失败时返回-1

int bind(int sockfd,struct sockaddr *my_addr,socklen_t addrlen);
//一般只有服务器端的程序调用,参数my_addr指定了sockfd将绑定到的本地
//地址,可以将参数my_addr的sin_addr设置为INADDR_ANY而不是某个确定
//IP地址就可以绑定到任何网络接口。

4、把套接字转化为被动监听函数:成功时返回0,失败时返回-1

int listen(int s,int backlog);
//参数s为套接字,参数backlog指定链接请求队列的最大长度;

5、接收连接请求函数:成功时返回文件描述符,失败时返回-1

int accept(int s,struct sockaddr *addr,socklen_t *addrlen);
//参数s是由函数socket创建,经函数bind绑定到本地某一端口上,然后通过
//函数listen转化而来的监听套接字
//参数addr用来保存发起连接请求的主机的地址和端口
//参数addrlen是addr所指向的结构体的大小

6、在TCP套接字上发送数据函数:有连接

包含3要素:套接字s,待发数据msg,数据长度len

ssize_t send(int s,const void *msg,size_t len,int flags);
//函数只能对处于连接状态的套接字使用,参数s为已建立好连接的套接字描述
//符,即accept函数的返回值
//参数msg指向存放待发送数据的缓冲区
//参数len为待发送数据的长度,参数flags为控制选项,一般设置为0

7、在TCP套接字上接收数据函数:有连接

包含3要素:套接字s,接收缓冲区buf,长度len

ssize_t recv(int s,void *buf,size_t len,int flags);
//函数recv从参数s所指定的套接字描述符(必须是面向连接的套接字)上接收
//数据并保存到参数buf所指定的缓冲区
//参数len则为缓冲区长度,参数flags为控制选项,一般设置为0

8、在UCP套接字上发送数据函数:无连接

ssize_t sendto(int s,const void *msg,size_t len,int flags,const struct sockaddr *to,socklen_t tolen);
//函数功能与函数send类似,但函数sendto不需要套接字处于连接状态,所以
//该函数通常用来发送UDP数据,同时因为是无连接的套接字,在使用sendto时
//需要指定数据的目的地址,参数msg指向待发送数据的缓冲区。
//参数len指定了待发送数据的长度
//参数flags是控制选项,含义与send函数中的一致
//参数to用于指定目的地址,目的地址的长度由tolen指定

9、在UDP套接字上接收数据函数:无连接

ssize_t recvfrom(int s ,void *buf,size_t len,int flags,struct sockaddr *from,socklen_t *fromlen);
//与函数recv功能类似,只是函数recv只能用于面向连接的套接字,而函数
//recvfrom没有此限制,可以用于从无连接的套接字上接收数据
//参数buf指向接收缓冲区
//参数len指定了缓冲区的大小
//参数flags是控制选项,含义与recv中的一致
//如果参数from非空,且该套接字不是面向连接的,则函数recvfrom返回时,
//参数from中将保存数据的源地址
//参数fromlen在调用recvfrom前为参数from的长度,调用recvfrom后将
//保存from的实际大小

10、关闭套接字函数:

int close(int fd);
//参数fd为一个套接字描述符;

11、多路复用函数:

int select(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
//参数n是需要监视的文件描述符数
//参数readfds指定需要监视的可读文件描述符集合
//参数writefds指定需要监视的可写文件描述符集合
//参数exceptfds指定需要监视的异常文件描述符的集合
//参数timeout指定了阻塞的时间

3、服务器端套接字(接收连接请求的套接字)创建过程

第一步:调用socket函数创建套接字。

第二步:调用bind函数分配IP地址和端口号。

第三部:调用listen函数转为可接收请求状态。

第四步:调用accept函数受理连接请求。

4、客户端套接字(发送连接请求的套接字)创建过程

只有两步:

1、调用socket函数创建套接字。

2、调用connect函数向服务器端发送连接请求。

5、Linux的文件操作

文件描述符:是系统自动分配给文件或套接字的整数。

每当生成文件或套接字,操作系统就会自动返回给我们一个整数。这个整数就是文件描述符,即创建的文件或套接字的别名,方便称呼而已。文件描述符在Windows中又称为句柄。

1、打开文件:

2、关闭文件或套接字:

3、将数据写入文件:

4、读取文件中的数据:

注:ssize_t = signed int, size_t = unsigned int,都是通过typedef声明,为基本数据类型取的别名。既然已经有了基本数据类型,那么为什么还需要为它取别名呢?是因为目前普遍认为int是32位的,而过去16位操作系统时代,int是16位的。根据系统的不同,时代的变化,基本数据类型的表现形式也随着变化的如果为基本数据类型取了别名,以后要修改,也就只需要修改typedef声明即可,这将大大减少代码变动。

6、服务器端实例

服务器端受理连接请求的程序。编译并运行该程序,创建等待连接请求的服务器端。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message);

int main(int argc, const char * argv[])
{
    int serv_sock;
    int clnt_sock;

    struct sockaddr_in serv_addr;
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size;

    char message[] = "Hello World!";

    if(argc != 2)
    {
        printf("Usage:%s <port>\n", argv[0]);
        exit(1);
    }
    //(1)调用socket函数创建套接字
    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    if(serv_sock == -1)
        error_handling("socket() error");

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(atoi(argv[1]));
    //(2)调用bind函数分配IP地址和端口号
    if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
        error_handling("bind() error");
    //(3)调用listen函数转为可接收请求状态
    if(listen(serv_sock, 5) == -1)
        error_handling("listen() error");

    clnt_addr_size = sizeof(clnt_addr);
    //(4)调用accept函数受理连接请求.没有连接请求时调用不会返回,直到有请求
    clnt_sock = accept(serv_sock, (struct sockaddr*) &clnt_addr, &clnt_addr_size);
    if(clnt_sock == -1)
        error_handling("accept() error");
    //(5)write函数用于传输数据。执行到此行说明已有了连接请求
    //向文件描述符为clnt_sock的客户端文件中传输message中的数据
    write(clnt_sock, message, sizeof(message));
    close(clnt_sock);//(6)
    close(serv_sock);//(7)

    return 0;
}
void error_handling(char *message)
{
    fputs(message, stderr);
    fputc(‘\n‘, stderr);
    exit(1);
}

7、客户端实例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message);

int main(int argc, const char * argv[])
{
    int sock;
    struct sockaddr_in serv_addr;
    char message[30];
    int str_len;

    if(argc != 3)
    {
        printf("Usage: %s <IP> <port>\n", argv[0]);
        exit(1);
    }
    //(1)创建套接字.但此时并不马上分为客户端或服务器端
    sock = socket(PF_INET, SOCK_STREAM, 0);
    if(sock == -1)
        error_handling("socket() error");

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
    serv_addr.sin_port = htons(atoi(argv[2]));
    //(2)调用connect函数向服务器端发送连接请求.此时确定为客户端
    if(connect(sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
        error_handling("connect() error");
    //(3)调用read函数向自身声明的message数组中保存数据
    str_len = read(sock, message, sizeof(message) - 1);
    if(str_len == -1)
        error_handling("read() error");

    printf("Message from server: %s \n", message);
    close(sock);//(4)

    return 0;
}
void error_handling(char *message)
{
    fputs(message, stderr);
    fputc(‘\n‘, stderr);
    exit(1);
}

参考:《TCP/IP网络编程》

时间: 2024-10-12 22:14:18

Linux网络编程和套接字的相关文章

Linux网络编程——原始套接字实例:简单版网络数据分析器

通过<Linux网络编程--原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 MAC 头部(有线局域网) 注意:CRC.PAD 在组包时可以忽略 链路层数据包的其中一种情况: unsigned char msg[1024] = { //--------------组MAC--------14------ 0xb8, 0x88, 0xe3, 0xe1, 0x10, 0xe6, // dst

Linux网络编程——原始套接字实例:MAC 头部报文分析

通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 MAC 头部(有线局域网) 注意:CRC.PAD 在组包时可以忽略 链路层数据包的其中一种情况: 1 unsigned char msg[1024] = { 2 //--------------组MAC--------14------ 3 0xb8, 0x88, 0xe3, 0xe1, 0x10, 0xe6,

Linux网络编程——原始套接字编程

原始套接字编程和之前的 UDP 编程差不多,无非就是创建一个套接字后,通过这个套接字接收数据或者发送数据.区别在于,原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有的数据帧(数据包).另外,必须在管理员权限下才能使用原始套接字. 原始套接字的创建: int socket ( int family, int type, int protocol ); 参数: family:协议族 这里写 PF_PACKET type:  套接字类,这里写 SOCK_RAW pr

Linux网络编程——原始套接字能干什么?

通常情况下程序员接所接触到的套接字(Socket)为两类: (1)流式套接字(SOCK_STREAM):一种面向连接的 Socket,针对于面向连接的TCP 服务应用: (2)数据报式套接字(SOCK_DGRAM):一种无连接的 Socket,对应于无连接的 UDP 服务应用. 从用户的角度来看,SOCK_STREAM.SOCK_DGRAM 这两类套接字似乎的确涵盖了 TCP/IP 应用的全部,因为基于 TCP/IP 的应用,从协议栈的层次上讲,在传输层的确只可能建立于 TCP 或 UDP 协议

Linux 网络编程——原始套接字实例:MAC 地址扫描器

如果 A (192.168.1.1 )向 B (192.168.1.2 )发送一个数据包,那么需要的条件有 ip.port.使用的协议(TCP/UDP)之外还需要 MAC 地址,因为在以太网数据包中 MAC 地址是必须要有的.那么怎样才能知道对方的 MAC 地址?答案是:它通过 ARP 协议来获取对方的 MAC 地址. ARP(Address Resolution Protocol,地址解析协议),是 TCP/IP 协议族中的一个,主要用于查询指定 ip 所对应的的 MAC(通过 ip 找 MA

Linux 网络编程——原始套接字实例:发送 UDP 数据包

以太网报文格式: 详细的说明,请看<MAC 头部报文分析>. IP 报文格式: 详细的说明,请看<IP 数据报格式详解>. UDP 报文格式: 详细的说明,请看<UDP 数据报格式详解>. 校验和函数: /******************************************************* 功能: 校验和函数 参数: buf: 需要校验数据的首地址 nword: 需要校验数据长度的一半 返回值: 校验和 ********************

LINUX 网络编程 原始套接字

一 原始套接字 原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM.SOCK_DGRAM的套接字,它实现于系统核心.然而,原始套接字能做什么呢?首先来说,普通的套接字无法处理ICMP.IGMP等网络报文,而SOCK_RAW可以:其次,SOCK_RAW也可以处理特殊的IPv4报文:此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头.总体来说,SOCK_RAW可以处理普通的网络报文之外,还可以处理一些特殊协议报文以及操作IP层及其以上的数据. 既然SOCK_R

Linux网络编程--自定义套接字描述符判定函数issockettype

套接字描述符和通用文件描述符在形式上没有区别,那么如何判断一个文件描述符是否是套接字描述符呢?下面我们就简单的自定义一个函数issockettype,用于套接字描述符判定. #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> i

[python] 网络编程之套接字Socket、TCP和UDP通信实例

很早以前研究过C#和C++的网络通信,参考我的文章: C#网络编程之Tcp实现客户端和服务器聊天 C#网络编程之套接字编程基础知识 C#网络编程之使用Socket类Send.Receive方法的同步通讯 Python网络编程也类似.同时最近找工作笔试面试考察Socket套接字.TCP\UDP区别比较多,所以这篇文章主要精简了<Python核心编程(第二版)>第16章内容.内容包括:服务器和客户端架构.套接字Socket.TCP\UDP通信实例和常见笔试考题. 最后希望文章对你有所帮助,如果有不