流程图
网络传输流程:
TCP连接:
api
客户端:
- socket: 创建套接字
domain: AF_INET :IPv4
type: SOCK_STREAM(tcp)、SOCK_DGRAM(udp)
protocol: 0 默认协议
返回值:成功返回一个新的套接字,失败返回1,设置errno
int socket(int domain, int type, int protocol);
- connect: 连接服务器(客户端执行)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockdf:
参数是成功调用socket()返回的套接字。
addr:
传入参数,指定服务器端地址信息,含IP地址和端口号
addrlen:
传入参数,如IPV4传入sizeof(struct sockaddr_in)大小
如果调用成功,表示连接已经建立(三次握手完成),返回0。否则返回 1并将错误码代存放于全局变量errno
之中。
服务端:
- bind: 给本地套接字赋予地址和端口号
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:
socket套接字
addr:
填写IP地址、端口号、协议族等信息。
addrlen:
addr结构体实例的大小
返回值:
成功返回0,失败返回1, 设置errno
- listen : 给连接排队
int listen(int sockfd, int backlog);
sockfd:
参数为成功调用socket函数返回的套接字,并已经成功调用了bind()。
backlog:
参数告诉套接字在忙于处理上一个请求时,还可以接受多少个进入的请求,换句话说,这决定了挂起连接的队
列的大小。
- accept: 等待客户
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockdf:
参数为成功调用socket函数返回的套接字,并已经成功调用了 bind() 和 listen()
addr:
传出参数,返回连接客户端地址信息,含IP地址和端口号。(如果使用IPv4地址族,那么它需要我们填个指向
sockaddr_in结构的指针)
addrlen:
传入传出参数(值结果),传入sizeof(addr)大小,函数返回时返回真正接收到地址结构体的大小
返回值:
成功返回一个新的socket文件描述符,用于和客户端通信,失败返回1,设置errn
Socket IO:
- send:客户端/服务器发送数据
int send( SOCKET s, const char FAR *buf, int len, int flags );
// flag 通常为0
- recv:客户端/服务器接收数据
int recv( SOCKET s, char FAR *buf, int len, int flags);
- read
int read(int sockfs, void *buf, size_t nbytes);
- write
int write(int sockfd, void *buf, size_t nbytes);
阻塞IO/非阻塞IO
阻塞:等待某个条件的满足才能往下执行,非阻塞反之。
设置非阻塞模式:
int flag = fcntl(sockfd,F_GETFL,0);
flags |= O_NONBLOCK;
fcntl(sockfd,F_SETFL,flags);
阻塞IO:
accept(), 需要等待客户端连接, 之后继续执行
recv() 等待数据到来后继续执行
如果设置非阻塞模式,则不需要进行等待
code
连接一个客户端收发数据:
一个客户端连接对应一个accept,在listen()执行后,accept()未执行前,客户端进行连接,accept 可以成功连接,并返回新的socket的值 , while循环处理send/recv
连接多个客户端进行收发数据:
创建线程,循环读取数据, 在while循环中, 调用accept和线程
客户端:
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define SERVER_PORT 9999
int main(int argc, char *argv[])
{
struct sockaddr_in serveraddr;
int sockfd, n, ret;
char buf[] = "helloworld";
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &serveraddr.sin_addr);
serveraddr.sin_port = htons(SERVER_PORT);
ret = connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if(ret == ‐1)
perror("connect");
ret = send(sockfd, buf, strlen(buf),0);
if(ret == ‐1)
perror("connect");
close(sockfd);
return 0;
}
服务器端:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#define BUFFER_LENGTH 128
void *routine(void *arg) {
int clientfd = *(int *)arg;
while (1) {
unsigned char buffer[BUFFER_LENGTH] = {0};
int ret = recv(clientfd, buffer, BUFFER_LENGTH, 0);
if (ret == 0) {
close(clientfd);
break;
}
printf("buffer : %s, ret: %d\n", buffer, ret);
ret = send(clientfd, buffer, ret, 0); //
}
}
int main() {
// block
int listenfd = socket(AF_INET, SOCK_STREAM, 0); //
if (listenfd == -1) return -1;
// listenfd
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(9999);
if (-1 == bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) {
return -2;
}
#if 0 // nonblock
int flag = fcntl(listenfd, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(listenfd, F_SETFL, flag);
#endif
listen(listenfd, 10);
while (1) {
struct sockaddr_in client;
socklen_t len = sizeof(client);
int clientfd = accept(listenfd, (struct sockaddr*)&client, &len);
pthread_t threadid;
pthread_create(&threadid, NULL, routine, &clientfd);
}
}