C语言相关库函数

字符串:

strstr(* s,*p) 返回值如果是字符串则是第一次出现 p的之后所有的字符串;若返回值为整形,则为首次出现p的位置

strchr(*s,char a)返回首次出现a及之后的字符串若没有则为null;

strncpy(p,s,int size_t)从s中返回size_t个字节给p;

bzero(*s,int n)置字节字符串的前n个字符为 0

index(*s,char a)本质与strchr差不多

atoi 字符串转整数

strcspn(s,p)返回值为一个整数即从s开头知道含p字符串之前的字符长度

strca(s,p)连接两个字符串且中间无空格

标准:

perror(s) 用来将上一个函数发生错误的原因输出到标准设备(stderr),参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量errno的值来决定要输出的字符串。

文件/流:

fprintf是C/C++中的一个格式化库函数,位于头文件<cstdio>或<bits/stdc++.h>中,其作用是格式化输出到一个流/文件中;函数原型为int fprintf( FILE *stream, const char *format, [ argument ]…),fprintf()函数根据指定的格式(format)向输出流(stream)写入数据(argument)。

信号的相关处理函数:

signal() sig是传递给它的唯一参数。执行了signal()调用后,进程只要接收到类型为sig的信号,不管其正在执行程序的哪一部分,就立即执行func()函数。当func()函数执行结束后,控制权返回进程被中断的那一点继续执行。

socket编程:

struct sockaddr_in {


struct sockaddr_in {

  short int sin_family; /* Address family */

  unsigned short int sin_port; /* Port number */

  struct in_addr sin_addr; /* Internet address */

  unsigned char sin_zero[8]; /* Same size as struct sockaddr */

  };

//此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。但一般编程中并不直接针对此数据结构操作,而是使用另一个与sockaddr等价的数据结构

sin_port存储端口号(使用网络字节顺序),在linux下,端口号的范围065535,同时01024范围的端口号已经被系统使用或保留。

sin_addr存储IP地址,使用in_addr这个数据结构

sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。

s_addr按照网络字节顺序存储IP地址

而其中in_addr结构的定义如下:


typedef struct in_addr {

  union {

  struct{ unsigned char s_b1,s_b2, s_b3,s_b4;} S_un_b;

  struct{ unsigned short s_w1, s_w2;} S_un_w;

  unsigned long S_addr;

  } S_un;

  } IN_ADDR;

** sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向

sockaddr的结构体,并代替它。也就是说,你可以使用sockaddr_in建立你所需要的信息,

然后用memset函数初始化就可以了memset((char*)&mysock,0,sizeof(mysock));//初始化

socketaddr

其定义如下:


struct sockaddr {

  unsigned short sa_family; /* address family, AF_xxx */

  char sa_data[14]; /* 14 bytes of protocol address */

  };

sa_family :是2字节的地址家族,一般都是“AF_xxx”的形式,它的值包括三种:AF_INET,AF_INET6和AF_UNSPEC。

如果指定AF_INET,那么函数就不能返回任何IPV6相关的地址信息;如果仅指定了AF_INET6,则就不能返回任何IPV4地址信息。

AF_UNSPEC则意味着函数返回的是适用于指定主机名和服务名且适合任何协议族的地址。

如果某个主机既有AAAA记录(IPV6)地址,同时又有A记录(IPV4)地址,那么AAAA记录将作为sockaddr_in6结构返回,而A记录则作为sockaddr_in结构返回

通常用的都是AF_INET。

2.memset()

void *memset(void *s, int ch, size_t n);

函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。

memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 [1] 。

memset()函数原型是extern void *memset(void *buffer, int c, int count) buffer:为指针或是数组,c:是赋给buffer的值,count:是buffer的长度.

3.inet_addr方法可以转化字符串,主要用来将一个十进制的数转化为二进制的数,用途多于ipv4的IP转化。

in_addr_t inet_addr(const char* strptr);

返回:若字符串有效则将字符串转换为32位二进制网络字节序的IPV4地址,否则为INADDR_NONE

struct in_addr{

in_addr_t s_addr;

}

inet_pton()

include <sys/socket.h>

include <netinet/in.h>

include<arpa/inet.h>

int inet_pton(int af, const char *src, void *dst);

这个函数转换字符串到网络地址,第一个参数af是地址簇,第二个参数src是来源地址,第三个参数 dst接收转换后的数据。

inet_pton 是inet_addr的扩展,支持的多地址族有下列:

af = AF_INET

src为指向字符型的地址,即ASCII的地址的首地址(ddd.ddd.ddd.ddd格式的),函数将该地址转换为in_addr的结构体,并复制在*dst中。

af = AF_INET6

src为指向IPV6的地址,函数将该地址转换为in6_addr的结构体,并复制在*dst中。

如果函数出错将返回一个负值,并将errno设置为EAFNOSUPPORT,如果参数af指定的地址族和src格式不对,函数将返回0。

inet_pton是一个IP地址转换函数,可以在将IP地址在“点分十进制”和“二进制整数”之间转换而且,inet_pton和inet_ntop这2个函数能够处理ipv4和ipv6。算是比较新的函数了。

例程:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

int main (void)

{

char IPdotdec[20]; //存放点分十进制IP地址

struct in_addr s; // IPv4地址结构体

// 输入IP地址

printf("Please input IP address: ");

scanf("%s", IPdotdec);

// 转换

inet_pton(AF_INET, IPdotdec, (void *)&s);

printf("inet_pton: 0x%x\n", s.s_addr); // 注意得到的字节序

// 反转换

inet_ntop(AF_INET, (void *)&s, IPdotdec, 16);

printf("inet_ntop: %s\n", IPdotdec);

}

4.memcpy内存拷贝函数,memcpy函数的功能是从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中。

函数原型:void *memcpy(void *dest, const void *src, size_t n);

说明:

1.source和destin所指的内存区域可能重叠,但是如果source和destin所指的内存区域重叠,那么这个函数并不能够确保source所在重叠区域在拷贝之前不被覆盖。而使用memmove可以用来处理重叠区域。函数返回指向destin的指针.

2.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。

注意:source和destin都不一定是数组,任意的可读写的空间均可

4.htons

htons是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为高位字节存放在内存的低地址处。

网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释,网络字节顺序采用big-endian排序方式。

htonl()

include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);

将主机数转换成无符号长整型的网络字节顺序。本函数将一个32位数从主机字节顺序转换成网络字节顺序。

5.socket()创建一个套接口()。

include <sys/socket.h>

int socket( int af, int type, int protocol);

函数用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源。如果协议protocol未指定(等于0),则使用缺省的连接方式。

对于使用一给定地址族的某一特定套接口,只支持一种协议。但地址族可设为AF_UNSPEC(未指定),这样的话协议参数就要指定了。协议号特定于进行通讯的“通讯域”。

参数:

af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。

type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。

protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

6.connect()用于建立与指定socket的连接。

头文件: #include <sys/socket.h>

函数原型: int connect(SOCKET s, const struct sockaddr * name, int namelen);

参数:

s:标识一个未连接socket

name:指向要连接套接字的sockaddr结构体的指针

namelen:sockaddr结构体的字节长度

s参数指定一个未连接的数据报或流类套接口。如套接口未被捆绑,则系统赋给本地关联一个唯一的值,且设置套接口为已捆绑。请注意若名字结构中的地址域为全零的话,则connect()将返回WSAEADDRNOTAVAIL错误。

7.shutdown()是指禁止在一个套接口上进行数据的接收与发送。

禁止在一个套接口上进行数据的接收与发送。

参数:

s:用于标识一个套接口的描述字。

how:标志,用于描述禁止哪些操作。

Linux:

include<sys/socket.h>

int shutdown(int sockfd,int how);

linux下成功则返回0,错误返回-1,错误码errno:EBADF表示sockfd不是一个有效描述符;ENOTCONN表示sockfd未连接;ENOTSOCK表示sockfd是一个描述符而不是socket描述符。

how的方式有三种分别是:

SHUT_RD(0):关闭sockfd上的读功能,此选项将不允许sockfd进行读操作。

SHUT_WR(1):关闭sockfd的写功能,此选项将不允许sockfd进行写操作。

SHUT_RDWR(2):关闭sockfd的读写功能。

五子棋的socket编程

TCP和UDP编程理解:bind ,recv,recvfrom,send,sendto

一:bind

1:作为客户端,调用 connect ,那么这个 bind 函数是为了绑定到都固定 IP 和端口作为自己 socket 地址。

2:作为服务器,调用 accept ,bind 是用来绑定到监听固定的 socket 地址数据,对于外来 socket,只有来之对应 IP 并链接的目标端口号才可以链接本服务器。

二:TCP 和 UDP 通信收发函数区别(记住soket中是否存放有IP 和端口信息,TCP有,UDP无,同时TCP服务器需要多个 socket 对象,对应多个连接)

1:TCP用send 和 recv :

这是因为每个TCP都有一个连接,每次连接完成后,都会把连接的信息记录在

socket 中,这样每次收发数据都知道对方和自己的 IP 地址还有端口号,无需再次指定。 记录是发生在 accept 和 connect 调用完成后。

  • 1)connect 调用因为只有一个socket 连接,就记录在本地。

  • 2)而对于accept 由于有多个连接,所以 accept 会返回一个 socket 对象,对应一个TCP连接,记录对应的IP和端口。

2:UDP 利用 sendto() 和 recvfrom()

  • 1)recvfrom 会返回发送端的地址,这样对服务器来说,由于时 UDP socket 对象没有记录对应的IP和端口信息(记录也没有用,UDP不稳定,随时可能变化),会需要用到改地址给客户端来发送响应。

    对于客户端,由于每次始终是知道服务器IP地址和端口(和一个服务器交互),所以无需记录(除非UDP客户端需要和多个服务器交互,需要一一记录,才能确保交互正确)
    
  • 2)sendto

    由于没有记录IP 和端口在 socket 对象中,所以每次都需要指定接受方地址,
    
    无论是客户端和服务器都需要用
    原文作者:Edingburgh
    原文地址: https://www.jianshu.com/p/cc3f41d9958c
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞