C/C++教程

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

本文主要是介绍《TCP/IP网络编程》第13章,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

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

  • send&recv
    • 收发数据可选项
    • MSG_OOB
    • 紧急模式
    • 检查输入缓冲
  • readv&writev
  • Windows实现

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
这篇关于《TCP/IP网络编程》第13章的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!