八月的雨季 最後的冰吻

八月的雨季 最後的冰吻

流程图

网络传输流程:
Socket编程--TCP连接以及并发处理-LMLPHP
TCP连接:
Socket编程--TCP连接以及并发处理-LMLPHP

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);

	}
}
05-01 00:38