C语言 进程通讯 socket套接字(TCP/UDP)示例

这篇具有很好参考价值的文章主要介绍了C语言 进程通讯 socket套接字(TCP/UDP)示例。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

主机字节序(host-byte):指处理器存储数据的字节顺序,分两种

        大端存储(big-endian):低地址存储数据高位(符合书写规则),由ARM、Motorola等采用

        小端存储(little-endian):低地址存储数据低位(将数据不重要的部分保存在低地址,重要的部分保存在高地址),由Intel、AMD等采用

如何测出主机字节序

#include <stdio.h>
int main(){
    int a = 0x12345678;
    char *p = (char *)&a;
    if(*p == 0x78){
        puts("小端");
    }else if(*p == 0x12){
        puts("大端");
    }
    return 0;
}

网络字节序(network-byte):指在网络编程中存储数据的字节顺序(大端存储),将数据重要的部分保存在低地址,不重要的部分保存到高地址,端口号(固定进程号)IP地址都需要网络字节序

端口号一般由IANA (Internet Assigned Numbers Authority) 管理,类型为unsigned short,最大值是65535,可在 /etc/services 文件中查看系统已被占用的端口号。拥有网络通讯功能的进程都会有端口号,用于传输层向应用层传递数据时找到目标进程

1~1023(1~255为常见服务端口,256~1023端口通常由UNIX系统占用)为常用端口,已被世界公认的各种服务征用,最好不要使用

1024~49151 为已登记端口,默认是给服务器程序使用的,但个人电脑一般不会安装服务器程序,只会安装客户端程序,所以平时编写程序时可以使用

49152~65535 为动态或私有端口,给客户端程序使用,一般是自动分配,因为客户端不绑定固定的IP和端口

端口号由主机字节序转换为网络字节序,可能需要配合 atoi() (字符串转换为整型)函数,头文件<netinet/in.h>:

16位无符号数转换

u_short htons(u_short short)

32位无符号数转换

u_long htonl(u_long hostlong)

返回值:网络字节序的无符号数(不是字符串)

端口号由网络字节序转换为主机字节序:

16位无符号数转换

u_short ntohs(u_short short)

32位无符号数转换

u_long ntohl(u_long hostlong)

返回值:主机字节序的无符号数(不是字符串)

IP地址的转换,头文件<arpa/inet.h>:

点分十进制的IPv4地址(127.0.0.1)被视为一个4字节 int 整型(32位),把IP地址的四个部分都分别转换成一个8位的二进制数(01111111   00000000   00000000   00000001),再直接合一起成为一个数(2130706433),就是IP地址

将IP地址的点分十进制转换成网络字节序

此方法有弊端,若字符串有效,返回32位网络字节序二进制数,否则返回INADDR_NONE = (32个1),占用了255.255.255.255

in_addr_t inet_addr(const char *strptr)

返回值:永远成功,返回网络字节序的32位无符号 int 型 IP 地址

IPv4也可以用这个方法

int inet_aton(const char *strptr, struct in_addr *addrptr)

strptr:IP地址的点分十进制字符串

addrptr:存储转换好的32位的网络字节序二进制数

返回值:成功 = 1,失败 = 0

将网络字节序的IP地址转换成点分十进制

char *inet_ntoa(struct in_addr inaddr)

返回值:一个点分十进制数 字符串的指针

随着IPv6(16字节 128位)出现了两个新函数,兼容IPv4地址和IPv6地址,函数名中p是表达(presentation),n是数值(numeric)

将IP地址的点分十进制转换成网络字节序

int inet_pton(int family, const char *strptr, void *addrptr)

family:IPv4或6,填 AF_INET 或 AF_INET6

strptr:IP地址的点分十进制字符串

addrptr:存储转换好的网络字节序二进制数

返回值:成功 = 1,非有效字符串 = 0,失败 = -1

示例

sockaddr_in addrServer;
inet_pton(AF_INET, "127.0.0.1", &addrServer.sin_addr);
addrServer.sin_port = htons(6666);  // 该函数需要添加头文件: #include <winsock.h>
addrServer.sin_family = AF_INET;

将网络字节序的IP地址转换成点分十进制

const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len)

family:IPv4或6,填 AF_INET 或 AF_INET6

addrptr:网络字节序的IP地址

strptr:存储转换好的IP地址点分十进制数,必须分配内存,指定大小

len:strptr的大小,以免溢出,若len太小不足以容纳转换的结果(包括结尾 ‘\0’ ),则返回空指针,置位错误码errno = ENOSPC

返回值:成功 = strptr,失败 = NULL

示例

char buf[20] = {0};
inet_ntop(AF_INET, &recvaddr.sin_addr, buf, sizeof(buf));

TCP/IP协议族

TCP/IP协议族之所以能实现网络通讯,依靠的是网络层的IP协议,只是IP协议仅能做最基本的通讯,缺乏很多精细的功能,这需要传输层的TCP或UDP来提供

c语言socket,tcp/ip,c语言,udp

c语言socket,tcp/ip,c语言,udp

c语言socket,tcp/ip,c语言,udp

c语言socket,tcp/ip,c语言,udp

应用层:

HTTP(Hypertext Transfer Protocol) 超文本传输协议:万维网的数据通信的基础

FTP(File Transfer Protocol) 文件传输协议:网络上文件传输的一套标准协议,使用TCP传输

TFTP(Trivial File Transfer Protocol) 简单文件传输协议:网络上文件传输的一套标准协议,使用UDP传输

SMTP(Simple Mail Transfer Protocol) 简单邮件传输协议:可靠且有效的电子邮件传输的协议

传输层:

TCP(Transport Control Protocol) 传输控制协议:面向连接的、有序的、双向通讯的、可靠的字节流通讯,并支持带外数据

UDP(User Datagram Protocol) 用户数据报协议:无连接的、不可靠的、固定长度的数据报通讯

网络层:

IP(Internetworking Protocol) 网际互连协议:在多个不同网络间实现信息传输的协议

ICMP(Internet Control Message Protocol) 互联网控制信息协议:在IP主机、路由器之间传递控制消息----ping命令

IGMP(Internet Group Management Protocol) 互联网组管理:组播协议,用于主机和组播路由器之间通信

链路层:

ARP(Address Resolution Protocol) 地址解析协议:通过IP地址获取对方mac地址

RARP(Reverse Address Resolution Protocol) 逆向地址解析协议:通过mac地址获取对方ip地址

TCP流程

c语言socket,tcp/ip,c语言,udp

创建一个套接字文件,将复杂的网络通信过程简化成普通的文件I/O操作过程,打开文件 -> 读/写 -> 关闭文件

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol)

domain:协议族,AF_xxx是地址族(address family),指明用什么协议的IP地址格式,PF_xxx是协议族(protocol family),指明用什么协议族,但是两种宏的值是一样的(如AF_UNIX == PF_UNIX),这是因为每种协议族都各只有一种IP地址格式,因此它们其实是一一对应的关系

AF_PACKET

原始套接字
AF_UNIX 或 AF_LOCAL 本机进程间通讯

AF_INET

IPv4

AF_INET6

IPv6
PF_IPX(几乎不用) novell 协议族,局域网专用,效率高,windows似乎不支持,linux支持
PF_APPLETALK 苹果公司的局域网协议族
AF_UNSPEC 不指定具体协议族,通过第三个参数protocol协议编号来指定用什么协议族中的哪个子协议

type:套接字类型,进一步指定协议族中的哪个子协议

SOCK_RAW

原始网络通讯(在TCP/IP协议族中是跳过传输层直接到网络层的IP协议,用IP协议通讯,一般用于想由自行编写的应用程序来实现类似传输层的TCP或UDP的功能的情况)

SOCK_STREAM

(流式套接字)

使用面向连接的、有序的、双向通讯的、可靠的字节流通讯,并支持带外数据(在TCP/IP协议族中只有TCP(内置流量控制,避免数据流淹没慢的接收方,数据无长度限制)符合)

SOCK_DGRAM

(数据报套接字)

使用无连接的、不可靠的、固定长度的数据报通讯(在TCP/IP协议族中只有UDP符合)
SOCK_NONBLOCK 对socket函数返回的文件描述符文件所执行的任何操作均由默认的阻塞改为非阻塞,可以和前面的宏一起用 | 运算
SOCK_CLOEXEC 一旦进程调用exec执行新进程后,自动关闭socket函数返回的文件描述符,可以和前面的宏一起用 | 运算

protocol:协议编号,每个协议的唯一识别号。0 = 使用 domain 和 type 参数指定的协议,但当 domain 和 type 所指的协议有多个选择时,需要该参数来区分,所有协议的协议编号都保存在 /etc/protocols 下

返回值:成功 = 文件描述符,失败 = -1 并置位errno错误码

将指定了通讯协议的socket套接字文件与自己的IP、端口进行绑定,建立固定的对应关系

服务端的TCP/IP协议族需要bind,而本机进程通讯需要bind到系统绝对路径下的某个文件上。若不明确的调用bind,即未完成socket套接字文件与固定的IP和端口绑定,则会自动指定一个IP和端口,且每次通讯都会重新指定,因此这种TCP服务器会导致客户端的三次握手失败

客户端的TCP/IP协议族不需要bind,但本机进程通讯需要bind,TCP的connect会三次握手且内核会自动生成本地端口并带上本地IP;UDP的sendto有目标地址和目标端口,内核会自动生成本地端口且带上本地IP;而本机进程通讯的客户端只能通过bind到同一个系统绝对路径文件才能发送出去,所以需要bind

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

sockfd:socket函数返回的文件描述符

addr:要绑定的自己的网络信息结构体,把struct sockaddr_in结构体(IPv4地址结构)强转为struct sockaddr结构体(通用地址结构)

struct sockaddr {
    unsigned short sa_family; // 地址族
    char sa_data[14];         // 端口号和IP地址
};
struct sockaddr_in {
    unsigned short sin_family;    //地址族
    unsigned short sin_port;      //端口号(网络字节序)htons(8888)
    struct in_addr sin_addr;      //IP地址(网络字节序)inet_addr("127.0.0.1")
    unsigned char sin_zero[8];    //为了使sockaddr与sockaddr_in保持相同大小而保留的空字节
};
struct in_addr {
    unsigned int s_addr; // IP地址
};

addrlen:网络信息结构体字节数

返回值:成功 = 0,失败 = -1 并置位errno错误码

将socket套接字设置成被动的,监听客户端连接的状态,只用于TCP服务器。socket套接字文件描述符默认是主动的,即可以主动向对方发送数据,转换为被动后,只能在接收到数据后才能应答,这样才能实现TCP连接的三次握手章程

int listen(int sockfd, int backlog)

sockfd:socket函数返回的文件描述符

backlog:监听队列长度,记录正在连接,但未连接成功的客户端,小于30即可

返回值:成功 = 0,失败 = -1 并置位errno错误码

阻塞被动监听客户端的连接,三次握手成功后,会返回一个新文件描述符,专门用于和该客户端通讯,对于正在连接的客户端会被记录到监听队列中,若 socket 函数中 type 参数设置为SOCK_NONBLOCK 非阻塞,则此函数也变为非阻塞,只用于TCP服务器

int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len)

sockfd:已被listen函数转换为被动监听的文件描述符,若未转换,失败

address:用于记录存放发起连接请求的客户端的 IP 和端口,需把struct sockaddr_in结构体(IPv4地址结构)强转为struct sockaddr结构体(通用地址结构),也可传NULL

address_len:客户端信息结构体字节数,但要求传的是地址,所以之前需要申请一个 socklen_t 类型的参数来获取 sizeof(clientaddr) 的值,若address填NULL,此处也填NULL

返回值:成功 = 与该客户端通讯专用的新文件描述符,失败 = -1 并置位errno错误码

只用于TCP客户端,客户端向服务器发起连接请求

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

sockfd:socket函数返回的文件描述符

addr:存放服务器网络信息的结构体,用于记录连接的哪个服务器

addrlen:网络信息结构体字节数

返回值:成功 = 0,失败 = -1 并置位errno错误码

发送消息,属于系统调用

ssize_t send(int sockfd, const void *buf, size_t len, int flags)

sockfd:accept函数(服务器)或socket函数(客户端)返回的文件描述符

buf:要发送的消息首地址,正规操作应当使用结构体来封装数据,并注意要主机字节序和网络字节序的转换,需强转成 void * 类型并 & 取址

len:buf 参数的字节数

flags:

0 (常用) 忽略此参数,send 函数与 write 函数效果相同
MSG_CONFIRM 告诉链路层已成功获得另一方的回复,若链路层没有检测到,将重新检测,仅对数据报套接字 SOCK_DGRAM 和 原始网络通讯 SOCK_RAW 有效,仅对 IPv4 和 IPv6 实现
MSG_DONTROUTE 不要使用网关来发送数据,只发送到直接连接的主机上。通常只有诊断或者路由程序会使用,这只针对路由的协议族定义的,数据包的套接字没有
MSG_DONTWAIT (常用) 非阻塞,若将被阻塞则调用失败返回 EAGAIN 或 EWOULDBLOCK
MSG_EOR 终止一条记录,只有部分套接字支持此功能如 SOCK_SEQPACKET
MSG_MORE

TCP中使用:调用方有更多的数据要发送,每次的调用都可设置

UDP中使用:告诉内核将所有设置了此标志位要发送的数据打包成一个单一的数据报,此数据报只有在执行没有设置此标志位的调用时才传输

MSG_NOSIGNAL (常用) 面向流的套接字中对端已关闭,不要生成SIGPIPE信号,返回EPIPE错误。此设置影响此进程中所有线程
MSG_OOB (常用) 发送带外数据,需要套接字类型支持此功能(如 SOCK_STREAM 类型),也需要底层协议也支持带外数据

返回值:成功 = 发送的字节数,失败 = -1 并置位errno错误码,超时或对端主动关闭 = 0

接收消息

ssize_t recv(int sockfd, void *buf, size_t len, int flags)

sockfd:accept函数(服务器)或socket函数(客户端)返回的文件描述符

buf:存放接收的消息的缓冲区,并注意要网络字节序和主机字节序的转换

len:buf 参数的字节数

flags:

0 (常用) 忽略此参数,recv 函数与 read 函数相同
MSG_CMSG_CLOEXEC 设置close-on-exec标志位,在执行exec函数族后关闭返回的套接字描述符
MSG_DONTWAIT (常用) 非阻塞,若将被阻塞则调用失败并返回EAGAIN 或 EWOULDBLOCK
MSG_ERRQUEUE 从套接字错误队列中接收错误信息
MSG_OOB (常用) 接收带外数据,需要套接字类型支持此功能(如 SOCK_STREAM 类型),也需要底层协议也支持带外数据
MSG_PEEK 接收操作改为返回接收队列的首位数据,但不将该数据从队列中移除。因此后续的接收调用将返回相同的数据
MSG_TRUNC 用于原始套接字(AF_PACKET)、Internet数据报、netlink、UNIX数据报套接字中,返回数据包或数据报的实际长度,即使它比传递的缓冲区长
MSG_WAITALL 阻塞等待直到完成整个请求,但在被信号打断、发生错误或断开连接、下一个要接收的数据与返回的数据类型不同时,会返回少于请求的数据。此标志对数据报套接字不起作用。

返回值:成功 = 接收的字节数,失败 = -1 并置位errno错误码,超时或对端主动关闭 = 0

close 函数断开连接

缺点:

        1.会将 读写 全关闭,无法实现分开关闭

        2.若一个连接中有多个文件描述符(1.dup复制;2.子进程从父进程中继承)时,只有关闭了所有文件描述符后连接才会关闭

int close(int fd)

shutdown 函数断开连接(正规方法)

可按照要求关闭连接,且只要 shutdown 关闭其中一个文件描述符,整个连接会立即断开

#include <sys/socket.h>
int shutdown(int sockfd, int how)

sockfd:accept函数(服务器)或socket函数(客户端)返回的文件描述符

how:如何断开连接

SHUT_RD 断开读
SHUT_WR 断开写
SHUT_RDWR 断开读写

返回值:成功 = 0,失败 = -1 并置位errno错误码

示例

服务端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>

#define ERRLOG(errmsg) do{\
                            perror(errmsg);\
                            printf("%s-%s(%d)\n",__FILE__,__func__,__LINE__);\
                            exit(-1);\
                        }while(0)

int main(int argc, char const *argv[])
{
    if(3 != argc){
        printf("Usage : %s <ip> <port>\n",argv[0]);
        exit(-1);
    }

    int socketfd = socket(AF_INET,SOCK_STREAM,0);
    if(-1 == socketfd){
        ERRLOG("socket error");
    }

    struct sockaddr_in serveraddr = {0};
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    if(-1 == bind(socketfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))){
        ERRLOG("bind error");
    }

    if(-1 == listen(socketfd,5)){
        ERRLOG("listen error");
    }

    struct sockaddr_in clientaddr;
    memset(&clientaddr,0,sizeof(clientaddr));

    socklen_t clientaddrlen = sizeof(clientaddr);
    char buff[128] = {0};
    int acceptfd = 0;
    int recvfd = 0;
    while(1){
        acceptfd = accept(socketfd,(struct sockaddr*)&clientaddr,&clientaddrlen);
        if(-1 == acceptfd){
            ERRLOG("accept error");
        }
        printf("客户端 %s:%d 连接到服务器了\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));

        while(1){
            if(0 > (recvfd = recv(acceptfd,buff,128,0))){
                ERRLOG("recv error");
            }if(0 == recvfd){
                printf("客户端 %s:%d 断开了连接\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
                break;
            }else{
                if(0 == strcmp(buff,"quit")){
                    printf("客户端 %s:%d 退出了\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
                    break;
                }
                printf("%s-%d:[%s]\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port),buff);
                strcat(buff,"--client");
                if(-1 == send(acceptfd,buff,128,0)){
                    ERRLOG("send error");
                }
            }
        }
        close(acceptfd);
    }
    shutdown(socketfd, SHUT_RDWR);
    return 0;
}

客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>

#define ERRLOG(errmsg) do{\
                            perror(errmsg);\
                            printf("%s-%s(%d)\n",__FILE__,__func__,__LINE__);\
                            exit(-1);\
                        }while(0)
                    
int main(int argc, char const *argv[])
{
    if(3 != argc){
        printf("Usage : %s <ip> <port>\n",argv[0]);
        exit(-1);
    }

    int socketfd = socket(AF_INET,SOCK_STREAM,0);
    if(-1 == socketfd){
        ERRLOG("socker error");
    }

    struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = ntohs(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    if(-1 == connect(socketfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))){
        ERRLOG("connect error");
    }

    char buff[128] = {0};
    while(1){
        fgets(buff,128,stdin);
        buff[strlen(buff)-1] = '\0';
        if(-1 == send(socketfd,buff,128,0)){
            ERRLOG("send error");
        }
        if(0 == strcmp(buff,"quit")){
            break;
        }
        if(-1 == recv(socketfd,buff,128,0)){
            ERRLOG("recv error");
        }
        printf("收到回复:[%s]\n",buff);
    }
    close(socketfd);

    return 0;
}

UDP流程

c语言socket,tcp/ip,c语言,udp

UDP中发送消息

在TCP(SOCK_STREAM, SOCK_SEQPACKET)下使用,参数dest_addr和addrlen被忽略,若不置为null和0,则返回错误EISCONN,若置为NULL和0后并不处于连接状态,同样返回错误EISCONN

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)

sockfd:socket函数返回的文件描述符

buf:要发送的消息首地址,正规操作应当使用结构体来封装数据,并注意要主机字节序和网络字节序的转换,需强转成 void * 类型并 & 取址

len:buf 参数的字节数

flags:

0 (常用) 忽略此参数
MSG_CONFIRM 告诉链路层已成功获得另一方的回复,若链路层没有检测到,将重新检测,仅对数据报套接字 SOCK_DGRAM 和 原始网络通讯 SOCK_RAW 有效,仅对 IPv4 和 IPv6 实现
MSG_DONTROUTE 不要使用网关来发送数据,只发送到直接连接的主机上。通常只有诊断或者路由程序会使用,这只针对路由的协议族定义的,数据包的套接字没有
MSG_DONTWAIT (常用) 非阻塞,若将被阻塞则调用失败返回 EAGAIN 或 EWOULDBLOCK
MSG_EOR 终止一条记录,只有部分套接字支持此功能如 SOCK_SEQPACKET
MSG_MORE

TCP中使用:调用方有更多的数据要发送,每次的调用都可设置

UDP中使用:告诉内核将所有设置了此标志位要发送的数据打包成一个单一的数据报,此数据报只有在执行没有设置此标志位的调用时才传输

MSG_NOSIGNAL (常用) 面向流的套接字中对端已关闭,不要生成SIGPIPE信号,返回EPIPE错误。此设置影响此进程中所有线程
MSG_OOB (常用) 发送带外数据,需要套接字类型支持此功能(如 SOCK_STREAM 类型),也需要底层协议也支持带外数据

dest_addr:接收方的网络信息结构体,需把struct sockaddr_in结构体(IPv4地址结构)强转为struct sockaddr结构体(通用地址结构),表示发送给谁

addrlen:网络信息结构体字节数

返回值:成功 = 发送的字节数,失败 = -1 并置位errno错误码,超时或对端主动关闭 = 0

UDP中接收消息

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)

sockfd:socket函数返回的文件描述符

buf:存放接收的消息的缓冲区,并注意要网络字节序和主机字节序的转换

len:buf 参数的字节数

flags:

0 (常用) 忽略此参数
MSG_CMSG_CLOEXEC 设置close-on-exec标志位,在执行exec函数族后关闭返回的套接字描述符
MSG_DONTWAIT (常用) 非阻塞,若将被阻塞则调用失败并返回EAGAIN 或 EWOULDBLOCK
MSG_ERRQUEUE 从套接字错误队列中接收错误信息
MSG_OOB (常用) 接收带外数据,需要套接字类型支持此功能(如 SOCK_STREAM 类型),也需要底层协议也支持带外数据
MSG_PEEK 接收操作改为返回接收队列的首位数据,但不将该数据从队列中移除。因此后续的接收调用将返回相同的数据
MSG_TRUNC 用于原始套接字(AF_PACKET)、Internet数据报、netlink、UNIX数据报套接字中,返回数据包或数据报的实际长度,即使它比传递的缓冲区长
MSG_WAITALL 阻塞等待直到完成整个请求,但在被信号打断、发生错误或断开连接、下一个要接收的数据与返回的数据类型不同时,会返回少于请求的数据。此标志对数据报套接字不起作用。

src_addr:发送方的网络信息结构体,需把struct sockaddr_in结构体(IPv4地址结构)强转为struct sockaddr结构体(通用地址结构),表示从谁那接收

addrlen:发送方的网络信息结构体字节数,但要求传的是地址,所以之前需要申请一个 socklen_t 类型的参数来获取 sizeof(clientaddr) 的值

返回值:成功 = 接收的字节数,失败 = -1,超时或对端主动关闭 = 0

示例

服务端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char const *argv[])
{
    if(3 != argc){
       printf("Usage : %s <ip> <port>\n",argv[0]);
       exit(-1); 
    }

    int socketfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == socketfd){
        perror("socker error");
        printf("%s--%s(%d)\n",__FILE__,__func__,__LINE__);
        exit(-1);
    }

    struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    if(-1 == bind(socketfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))){
        perror("bind error");
        printf("%s--%s(%d)\n",__FILE__,__func__,__LINE__);
        exit(-1);
    }

    struct sockaddr_in clientaddr;
    memset(&clientaddr,0,sizeof(clientaddr));

    socklen_t clientaddrlen = sizeof(clientaddr);
    char buff[128] = {0};
    while(1){
        if(-1 == recvfrom(socketfd, buff, 128, 0, (struct sockaddr*)&clientaddr,&clientaddrlen)){
            perror("recvfrom error");
            printf("%s--%s(%d)\n",__FILE__,__func__,__LINE__);
            exit(-1);
        }
        printf("%s(%d):%s\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port),buff);
        strcat(buff,"--strcat");
        if(-1 == sendto(socketfd,buff,128,0,(struct sockaddr*)&clientaddr,clientaddrlen)){\
            perror("sendto error");
            printf("%s--%s(%d)\n",__FILE__,__func__,__LINE__);
            exit(-1);
        }
        memset(buff,0,128);
    }
    close(socketfd);

    return 0;
}

客户端文章来源地址https://www.toymoban.com/news/detail-755374.html

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <error.h>

int main(int argc, char const *argv[])
{   
    if(3 != argc){
        printf("Usage : %s <ip> <port>\n",argv[0]);
        exit(-1);
    }

    int socketfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == socketfd){
        perror("socket error");
        printf("%s--%s(%d)\n",__FILE__,__func__,__LINE__);
        exit(-1);
    }

    struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t serveraddrlen = sizeof(serveraddr);
    char buff[128] = {0};
    while(1){
        puts("input your msg");
        fgets(buff, 128 ,stdin);
        buff[strlen(buff)-1] = '\0';
        if(0 == strcmp(buff,"quit")){
            break;
        }
        if(-1 == sendto(socketfd, buff, 128 ,0 ,(struct sockaddr*)&serveraddr,serveraddrlen)){
            perror("sendto error");
            printf("%s--%s(%d)\n",__FILE__,__func__,__LINE__);
            exit(-1);
        }
        if(-1 == recvfrom(socketfd,buff,128,0,(struct sockaddr*)&serveraddr,&serveraddrlen)){
            perror("recvform error");
            printf("%s--%s(%d)\n",__FILE__,__func__,__LINE__);
            exit(-1);
        }
        printf("recv:[%s]\n",buff);
        memset(buff,0,128);
    }
    close(socketfd);

    return 0;
}

到了这里,关于C语言 进程通讯 socket套接字(TCP/UDP)示例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java网络编程基础:TCP Socket套接字编程 IntAddress UDP等...

    目录 一,网络基础 1.IP地址 2.端口 3.TCP/UDP协议 4.网络编程开发模式  二,基于套接字的Java网络编程 1.Socket  2.InetAddress 三.基于TCP的Socket网络编程 1.单服务器端与单Socket客户端一次通讯 2.单服务器端接收多次通讯  3.TCP网络通讯补充 四,基于UDP的网络编程 1. DatagramSocket:收发

    2024年04月29日
    浏览(46)
  • 【网络编程】详解UDP/TCP套接字的创建流程+守护进程

    需要云服务器等云产品来学习Linux的同学可以移步/--腾讯云--/--阿里云--/--华为云--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。   目录 一、网络编程套接字 1、一些概念 1.1源IP地址和目的IP地址 1.2端口号port 1.3TCP和UDP的性质 1.4网络字节序、IP地址类型转换

    2024年02月05日
    浏览(54)
  • python3套接字编程之socket和socketserver(TCP和UDP通信)

    socket和socketserver是python3中socket通信模块,关于其使用做如下总结。 目录 1.socket 1.1模块引入 1.2套接字获取 1.3套接字接口 1.3.1 服务端 1.3.2 客户端套接字函数 1.3.3 公共套接字函数 1.3.4 面向锁的套接字方法 1.3.5 面向文件的套接字的函数 2.socketserver 3.TCP 3.1 socket类型TCP 3.2 sockets

    2024年02月15日
    浏览(75)
  • 【网络编程】网络编程概念,socket套接字,基于UDP和TCP的网络编程

    前言: 大家好,我是 良辰丫 ,今天我们一起来学习网络编程,网络编程的基本概念,认识套接字,UDP与TCP编程.💞💞💞 🧑个人主页:良辰针不戳 📖所属专栏:javaEE初阶 🍎励志语句:生活也许会让我们遍体鳞伤,但最终这些伤口会成为我们一辈子的财富。 💦期待大家三连,关注

    2023年04月20日
    浏览(61)
  • 【Java】网络编程与Socket套接字、UDP编程和TCP编程实现客户端和服务端通信

    为什么需要网络编程? 现在网络普及程序越来越高,网络上保存着我们日常生活中需要的各种资源,使用程序通过网络来获取这些资源的过程就需要网络编程来实现。 什么是网络编程? 网络编程,指网络上的主机,通过不同的进程以程序的方式实现网络通信(网络数据传输)

    2024年02月17日
    浏览(78)
  • Socket套接字(UDP数据报)篇

    Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元. 基于Socket套接字的网络程序开发就是网络编程. 使用的是UDP(User Datagram Protocol)协议,传输层协议. 无连接 不可靠传输 面向数据报 全双工 对于UDP协议来说,具有无连接,面向数据报的特征

    2024年04月29日
    浏览(39)
  • 网络编程『socket套接字 ‖ 简易UDP网络程序』

    🔭个人主页: 北 海 🛜所属专栏: Linux学习之旅、神奇的网络世界 💻操作环境: CentOS 7.6 阿里云远程服务器 在当今数字化时代,网络通信作为连接世界的桥梁,成为计算机科学领域中至关重要的一部分。理解网络编程是每一位程序员必备的技能之一,而掌握套接字编程则

    2024年02月04日
    浏览(57)
  • 【网络】socket——预备知识 | 套接字 | UDP网络通信

    🐱作者:一只大喵咪1201 🐱专栏:《网络》 🔥格言: 你只管努力,剩下的交给时间! 在前面本喵对网络的整体轮廓做了一个大概的介绍,比如分层,协议等等内容,现在我们直接进入socket(套接字)编程,先来感受到网络编程。 我们知道,在网络通信中,存在两套地址,一

    2024年02月13日
    浏览(53)
  • 【JavaEE】网络编程之TCP套接字、UDP套接字

    目录 1.网络编程的基本概念 1.1为什么需要网络编程  1.2服务端与用户端 1.3网络编程五元组  1.4套接字的概念 2.UDP套接字编程 2.1UDP套接字的特点  2.2UDP套接字API 2.2.1DatagramSocket类 2.2.2DatagramPacket类  2.2.3基于UDP的回显程序 2.2.4基于UDP的单词查询  3.TCP套接字编程 3.1TCP套接字的特

    2023年04月20日
    浏览(75)
  • 【JaveEE】网络编程之TCP套接字、UDP套接字

    目录 1.网络编程的基本概念 1.1为什么需要网络编程  1.2服务端与用户端 1.3网络编程五元组  1.4套接字的概念 2.UDP套接字编程 2.1UDP套接字的特点  2.2UDP套接字API 2.2.1DatagramSocket类 2.2.2DatagramPacket类  2.2.3基于UDP的回显程序 2.2.4基于UDP的单词查询  3.TCP套接字编程 3.1TCP套接字的特

    2023年04月13日
    浏览(169)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包