一.基础知识
客户端与服务器之间将展开一段结构化对话,叫协议。
在C语言中,如果写一个与网络通信的程序,就需要新的数据流-套接字,套接字是双向的,既可以输入也可以输出
1.使用socket()函数创建一个套接字数据流
#include <sys/socket.h> int xxxx = socket(PF_INET, SOCK_STREAM, 0);
xxxx是套接字名字
套接字与客户端程序通信,服务器需要经历,绑定端口,监听,接受连接,开始通信,四个阶段
服务启动时,服务器会为每项服务分配一个端口,服务器启动时,需要告诉操作系统将要使用哪个端口,这个过程叫做端口绑定。
2.这些代码将创建一个表示"互联网xxx端口"的套接字名
#include <arpa/inet.h> struct sockeaddr_in xxxx; xxxx.sin_family = PF_INET; xxxx.sin+port = (in_port_t)htons(端口号); xxxx.sin_addr.s_addr = htonl(INADDR_ANY); int x = bind (套接字名字, (struct sockaddr * ) &xxxx, sizeof(name));
3.设置客户端连接服务器的排队数量
listen(套接字名字, xx)
xx为人数
在linux操作系统的/etc/services文件中可以查看常用服务使用的端口号
4.保存连接客户端的详细信息
struct sockaddr_storage xxxx; unsigned int address_size = sizeof(client_addr); int connect_d = accept(listener_d, (struct sockaddr *) &client_addr, &address_size);
套接字的输出方法:
二.使用send()函数写数据
send(套接字名称, xxxx, strlen(xxx), 0)
xxx和strlen(xxx)是消息和消息长度
0是高级参数填0即可
绑定端口有延时的解决办法,当使用某个端口绑定了套接字后,在接下来的30秒内,操作系统不允许任何程序绑定它,包括上次绑定这个端口的程序,我们在绑定前设置套接字的某个选项,就可以解决这个问题了。
int reuse = 1; setsockopt(套接字名字, SQL_SOCKET, SO_REUSEADDR, (char * )&reuse, sizeof(int));
三.使用recv()函数读数据
recv (接收的描述符,字符变量的缓冲区,<要读取几个字节>, 0);
1.字符串不以\0结尾
2.当用户在客户端输入文本时用\r\n结尾
3.recv()将返回字符个数,如果发生错误就返回-1,如果客户端关闭连接就返回0
4.recv调用不一定能一次接收所有字符,需要配合循环,读取字符。
例如:
int read_in (int socket, char * buf, int len) { int c = recv(socket, buf, len, 0); }
四.为每个客服端复制一份子进程
服务器在运行一份套接字进程给第一个用户时,无法运行第二个进程给第二个用户,因此使用fork()为每个用户创建一个子进程。
服务器父进程只需要用主监听套接字,因此关闭副套接字,子进程需处理副套接字,关闭主监听套接字
五.创建ip地址套接字
1.客户端套接字的创建:
int xxx = socket (PF_INET, SOCK_STREAM, 0);
2.客户端和服务器处理套接字的方法不同,服务器会把套接字绑定到本地端口,而客户端会把套接字连接至远程端口。
struct sockaddr_in xxx; memset(&xxx, 0, sizeof(xxx)); xxx.sin_family = PF_INET; xxx.sin_addr.s_addr = inet_addr("ip地址"); xxx.sin_port = htons(端口号); connect(xxx, (struct sockaddr *) &xxx, sizeof(xxx)); 连接套接字至远程端口
六.使用域名创建套接字
域名系统,计算机发送数据包时需要在地址一栏填写数字形式的ip地址,而dns可以把域名转化为ip地址
创建域名套接字:
#include<netdb.h> struct addrinfo * xxx; struct addrinfo xxxx; memset(&xxxx, 0, sizeof(xxxx)); xxxx.ai_family = PF_UNSPEC; xxxx.ai_socktype = SOCK_STREAM; getaddrinfo("域名", "端口", &xxxx, &xxx); getaddrinfo会在堆上创建一个xxx名字的新数据结构给定域名和端口号。
随后创建套接字
int x = socket(xxx->ai_family, xxx->ai_socktype, xxx->ai_protocal); connect(x, xxx->ai_addr, xxx->ai_addrlen); freeaddrinfo(xxx);连接以后删除地址数据