ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

《TCP/IP网络编程》第13章

2022-01-12 10:33:08  阅读:159  来源: 互联网

标签:13 addr int IP sock TCP len include listen


《TCP/IP网络编程》第13章

send&recv

#include <sys/socket.h>
//成功字节,失败-1
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);

//成功字节(收到EOF返回0),失败-1
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);

收发数据可选项

flags含义sendrecv
MSG_OOB用于传输带外数据(Out-of-band data)**
MSG_PEEK验证输入缓冲中是否存在接收的数据*
MSG_DONTROUTE数据传输过程中不参照路由(Routing)表,在本地(Local)网络中寻找目的地*
MSG_DONTWAIT非阻塞(Non-blocking)I/O**
MSG_WAITALL防止函数返回,直到接收全部请求的字节数*

MSG_OOB

MSG_OOB用于创建特殊发送方法和通道以发送紧急消息。

oob_send.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[]) {
	int sock;

	struct sockaddr_in addr;
	socklen_t addr_size;

	if(argc!=3) {
		printf("Usage : %s <IP> <port>\n", argv[0]);
		exit(1);
	}

	sock = socket(PF_INET, SOCK_STREAM, 0);
	if(sock==-1)
		error_handling("socket() error");

	addr_size = sizeof(addr);
	memset(&addr, 0, addr_size);
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(argv[1]);
	addr.sin_port = htons(atoi(argv[2]));
	
	if(connect(sock, (struct sockaddr*)&addr, addr_size)==-1)
		error_handling("connect() error");
	
	write(sock, "123", strlen("123"));
	send(sock, "4", strlen("4"), MSG_ODB);
	
	write(sock, "567", strlen("567"));
	send(sock, "890", strlen("890"), MSG_ODB);
	
	close(sock);
	return 0;
}


void error_handling(char *message) {
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}
gcc oob_send.c -o send
./send 127.0.0.1 9190

oob_recv.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>

#define BUF_SIZE 30
void error_handling(char *message);
void urg_handler(int signo);

int listen_sock;
int connect_sock;

int main(int argc, char *argv[]) {
	struct sockaddr_in listen_addr;
	struct sockaddr_in connect_addr;
	socklen_t addr_size;
	
	int str_len, state;

	struct sigaction act;
	char buf[BUF_SIZE];

	if(argc!=2) {
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}

	listen_sock= socket(PF_INET, SOCK_STREAM, 0);
	if(listen_sock==-1)
		error_handling("socket() error");

	addr_size = sizeof(struct sockaddr_in);
	
	memset(&listen_addr, 0, addr_size);
	listen_addr.sin_family = AF_INET;
	listen_addr.sin_addr.s_addr = inet_addr(argv[1]);
	listen_addr.sin_port = htons(atoi(argv[2]));
	
	if(bind(listen_sock, (struct sockaddr*)&listen_addr, addr_size)==-1)
		error_handling("bind() error");
	listen(listen_sock, 5);

	connect_sock=accept(listen_sock, (struct sockaddr*)&listen_addr, &addr_size);

	//fcntl函数控制文件描述符
	//指定connect_sock套接字的拥有者(F_SETOWN)为getpid()表示的进程
	//套接字的拥有者实际为操作系统
	//这里指负责套接字所有事务的主体
	//处理SIGURG信号的主体为当前进程,并非所有进程都会处理SIGURG信号
	//文件描述符connect_sock指向的套接字引发的SIGURG信号处理进程变为将getpid函数返回值用作ID的进程
	fcntl(connect_sock, F_SETOWN, getpid());
	act.sa_handler=urg_handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags=0;
	state=sigaction(SIGURG, &act, 0);
	//收到MSG_OOB紧急信息将产生SIGURG信号

	while((str_len=recv(connect_sock, buf, sizeof(buf), 0))!=0) {
		if(str_len==-1)
			continue;
		buf[str_len]=0;
		puts(buf);
	}
	
	close(connect_sock);
	close(listen_sock);
	return 0;
}


void error_handling(char *message) {
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}


void urg_handler(int signo) {
	int str_len;
	char buf[BUF_SIZE];
	str_len=recv(connect_sock, buf, sizeof(buf)-1, MSG_OOB);//一次urg_handler调用只能读取1个字节
	buf[str_len]=0;
	printf("Urgent message: %s\n", buf);
}
gcc oob_recv.c -o recv
./recv 9190

紧急模式

MSG_OOB(Out-of-band)并非真正意义上的"带外数据"(通过单独的通信路径高速传输数据)。
TCP不提供,可利用紧急模式(Urgen mode)传输。

TCP保持传输顺序的特性不会改变,MSG_OOB的真正意义在于督促数据接收对象尽快处理数据。

检查输入缓冲

MSG_DONTWAIT,非阻塞。
MSG_PEEK,recv读取缓冲数据后不删除。
recv+MSG_DONTWAIT|MSG_PEEK,非阻塞方式验证带读取数据是否存在。

peek_send.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

void error_handling(char *message);

int main(int argc, char *argv[]) {
	int sock;

	struct sockaddr_in addr;
	socklen_t addr_size;

	if(argc!=3) {
		printf("Usage : %s <IP> <port>\n", argv[0]);
		exit(1);
	}

	sock = socket(PF_INET, SOCK_STREAM, 0);
	if(sock==-1)
		error_handling("socket() error");

	addr_size = sizeof(struct sockaddr_in);
	
	memset(&addr, 0, addr_size);
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(argv[1]);
	addr.sin_port = htons(atoi(argv[2]));
	
	if(connect(sock, (struct sockaddr*)&addr, addr_size)==-1)
		error_handling("connect() error");
	
	write(sock, "123", strlen("123"));
	
	close(sock);
	return 0;
}


void error_handling(char *message) {
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}
gcc peek_send.c -o send
./send 127.0.0.1 9190

peek_recv.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[]) {
	int listen_sock;
	int connect_sock;
	struct sockaddr_in listen_addr;
	struct sockaddr_in connect_addr;
	socklen_t addr_size;
	
	int str_len, state;
	char buf[BUF_SIZE];

	if(argc!=2) {
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}

	listen_sock= socket(PF_INET, SOCK_STREAM, 0);
	if(listen_sock==-1)
		error_handling("socket() error");

	addr_size = sizeof(struct sockaddr_in);
	
	memset(&listen_addr, 0, addr_size);
	listen_addr.sin_family = AF_INET;
	listen_addr.sin_addr.s_addr = inet_addr(argv[1]);
	listen_addr.sin_port = htons(atoi(argv[2]));
	
	if(bind(listen_sock, (struct sockaddr*)&listen_addr, addr_size)==-1)
		error_handling("bind() error");
	listen(listen_sock, 5);

	connect_sock=accept(listen_sock, (struct sockaddr*)&listen_addr, &addr_size);

	while(1) {
		str_len=recv(connect_sock, buf, sizeof(buf)-1, MSG_PEEK|MSG_DONTWAIT);
		if(str_len>0)
			break;
	}
	buf[str_len]=0;
	printf("Buffering %d bytes: %s\n", str_len, buf);
	
	str_len=recv(connect_sock, buf, sizeof(buf)-1, 0);
	buf[str_len]=0;
	printf("Read again: %s\n", buf);

	close(connect_sock);
	close(listen_sock);
	return 0;
}


void error_handling(char *message) {
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}


void urg_handler(int signo) {
	int str_len;
	char buf[BUF_SIZE];
	str_len=recv(connect_sock, buf, sizeof(buf)-1, MSG_OOB);//一次urg_handler调用只能读取1个字节
	buf[str_len]=0;
	printf("Urgent message: %s\n", buf);
}
gcc oob_recv.c -o recv
./recv 9190

readv&writev

对数据进行整合传输及发送的函数。
writev将分散保存在多个缓存中的数据一并发送,readv由多个缓冲分别接收,可以减少I/O函数的调用次数。

#include <sys/uio.h>
//成功字节数,失败-1
ssize_t writev(int fileds, const struct iovec *iov, int iovcnt);
struct iovec {
	void *iov_base;//缓冲地址
	size_t iov_len;//缓冲大小
};

writev.c

#include <stdio.h>
#include <sys/uio.h>

int main(int argc, char *argv[]) {
	struct iovec vec[2];
	char buf1[]="ABCDEFG";
	char buf2[]="1234567";
	int str_len;
	
	vec[0].iov_base=buf1;
	vec[0].iov_len=3; 
	vec[1].iov_base=buf2;
	vec[1].iov_len=4; 

	str_len=writev(1, vec, 2);
	puts("");
	printf("Write bytes: %d\n", str_len);
	return 0;
}
gcc writev.c -o wv
./wv
//ABC1234
//Write bytes: 7
#include <sys/uio.h>
//成功字节数,失败-1
ssize_t readv(int fileds, const struct iovec *iov, int iovcnt);

readv.c

#include <stdio.h>
#include <sys/uio.h>

#define BUF_SIZE 100

int main(int argc, char *argv[]) {
	struct iovec vec[2];
	char buf1[BUF_SIZE]={0, };
	char buf2[BUF_SIZE]={0, };
	int str_len;
	
	vec[0].iov_base=buf1;
	vec[0].iov_len=5; 
	vec[1].iov_base=buf2;
	vec[1].iov_len=BUF_SIZE; 

	str_len=readv(0, vec, 2);
	printf("Read bytes: %d\n", str_len);
	printf("First message: %s\n", buf1);
	printf("Second message: %s\n", buf2);
	return 0;
}
gcc readv.c -o rv
./rv
I like TCP/IP socket programming
//Read bytes: 33
//First message: I lik
//Second message: e TCP/IP socket programming

Windows实现

Out-of-band数据属于异常(不同寻常的程序执行流)。Windows平台利用select的这一特性接收Out-of-band数据。

oob_send_win.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>

#define BUF_SIZE 30
void ErrorHanding(char *message);

int main(int argc, char *argv[]) {
	WSADATA wsaData;
	SOCKET sock;
	SOCKADDR_IN addr;
	int addr_size;

	if(argc!=3) {
		printf("Usage : %s <IP> <port>\n", argv[0]);
		exit(1);
	}

	if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0)
		ErrorHanding("WSAStartup() error!");
	
	sock = socket(PF_INET, SOCK_STREAM, 0);
	if(sock==INVALID_SOCKET)
		ErrorHanding("socket() error");

	addr_size = sizeof(SOCKADDR_IN);
	
	memset(&addr, 0, addr_size);
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(argv[1]);
	addr.sin_port = htons(atoi(argv[2]));
	
	if(connect(sock, (SOCKADDR*)&addr, addr_size)==SOCKET_ERROR)
		ErrorHanding("connect() error");
	
	send(sock, "123", strlen("123"), 0);
	send(sock, "4", strlen("4"), MSG_OOB);
	
	send(sock, "567", strlen("567"), 0);
	send(sock, "8905ab", strlen("8905ab"), MSG_OOB); //MSG_OOB只发生一个字符b,"8905a"普通字符串
	
	closesocket(sock);
	WSACleanup();
	return 0;
}

void ErrorHanding(char *message) {
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}
cl /EHsc oob_send_win.c /Fe:sendWin.exe /link ws2_32.lib
sendWin 127.0.0.1 9190

oob_recv_win.c

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>

#define BUF_SIZE 30
void ErrorHanding(char *message);

int main(int argc, char *argv[]) {
	WSADATA wsaData;
	SOCKET listen_sock;
	SOCKET connect_sock;
	SOCKADDR_IN listen_addr;
	SOCKADDR_IN connect_addr;
	int addr_size;
	
	int str_len;

	char buf[BUF_SIZE];
	int result;
	int i;

	fd_set reads, reads_copy;
	fd_set excepts, excepts_copy;
	struct timeval timeout;

	if(argc!=2) {
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}

	if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0)
		ErrorHanding("WSAStartup() error!");
		
	listen_sock = socket(PF_INET, SOCK_STREAM, 0);
	if(listen_sock==INVALID_SOCKET)
		ErrorHanding("socket() error");
	
	addr_size = sizeof(SOCKADDR_IN);
	
	memset(&listen_addr, 0, addr_size);
	listen_addr.sin_family = AF_INET;
	listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	listen_addr.sin_port = htons(atoi(argv[1]));
	
	if(bind(listen_sock, (struct sockaddr*)&listen_addr, addr_size)==SOCKET_ERROR)
		ErrorHanding("bind() error");

	if(listen(listen_sock, 5)==SOCKET_ERROR)
		ErrorHanding("listen() error");
	
	FD_ZERO(&reads);
	FD_SET(listen_sock, &reads);
	
	FD_ZERO(&excepts);
	FD_SET(listen_sock, &excepts);

	while(1) {
		reads_copy = reads;
		excepts_copy = excepts;
		timeout.tv_sec = 55;
		timeout.tv_usec = 5000;
		
		if((result = select(0, &reads_copy, 0, &excepts_copy, &timeout))==SOCKET_ERROR)
			break;
		
		if(result==0)
			continue;

		for(i=0; i<reads.fd_count; i++) {
			if(FD_ISSET(reads.fd_array[i], &excepts_copy)) {
				if(reads.fd_array[i]==listen_sock) {
					ErrorHanding("listen_sock error");
				} else {
					//MSG_OOB,一次发送一个字符
					//MSG_OOB,一次可以读取多个字符
					str_len = recv(reads.fd_array[i], buf, BUF_SIZE-1, MSG_OOB);
					buf[str_len] = 0;
					printf("Urgent message: %s\n", buf);
				}
			}
			if(FD_ISSET(reads.fd_array[i], &reads_copy)) {
				if(reads.fd_array[i]==listen_sock) {
					connect_sock = accept(listen_sock, (SOCKADDR*)&connect_addr, &addr_size);
					FD_SET(connect_sock, &reads);
					FD_SET(connect_sock, &excepts);
					printf("connected client: %d\n", connect_sock);
				} else {
					str_len = recv(reads.fd_array[i], buf, BUF_SIZE-1, 0);
					if(str_len==0) {
						FD_CLR(reads.fd_array[i], &reads);
						FD_CLR(reads.fd_array[i], &excepts);
						closesocket(reads.fd_array[i]);
						printf("closed client: %d\n", reads.fd_array[i]);
					} else {
						buf[str_len] = 0;
						puts(buf);
					}
				}
			}
		}
	}

	closesocket(listen_sock);
	WSACleanup();
	return 0;
}

void ErrorHanding(char *message) {
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}
cl /EHsc oob_recv_win.c /Fe:recvWin.exe /link ws2_32.lib
recvWin 9190

标签:13,addr,int,IP,sock,TCP,len,include,listen
来源: https://blog.csdn.net/oqqyx1234567/article/details/122416658

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有