网络编程学习笔记整理(二)

摘录笔记 《TCP/IP高效编程 改善网络程序的44个技巧》

socket的创建函数

int socket (int domain,int type, int protocol)

参数说明

protocol需要注意 常见有三个选项

SOCK_STREAM 创建TCP socket

SOCK_DGRAM 创建UDP socket

SOCK_RAW 对IP层的数据进行访问 例如监听ICMP 使用此种socket

 

TCP接收发送函数为 recv send 

linux下也可以使用read write函数

其中参数

MSG_OOB 表示发送读取紧急数据

MSG_PEEK 表示查看数据但是不会将其从缓冲中删除 下次依旧可以读取

MSG_DONTROUTE 表示内核绕过通常的选路函数 用于网络诊断

 

UDP接收发送函数使用 recvfrom sendto

 

面向连接与无连接的区别

无连接每个数据报是独立主体,相互间没有关联。网络传输中尽力正确传输数据,但是不保证数据不丢失、不乱序、不延迟

面向连接维护多个数据报间的关联,保证不发生乱序,并进行数据报到达的确认,以及超时重传机制

 

私有地址IP主机与因特网或者其他外网通讯使用NAT( Network Address Translation 网络地址翻译)

分为三种模式

1 静态地址 将私有网络的部分或者所有主机都映射为一个固定的全局分配的地址(少见)

2 地址池 NAT设备有一组全局分配的IP地址可用,会将其中之一动态分配给需要与外部网络对等通讯的主机

3 PAT Port Address Translation 端口地址转换 只有一个全局分配地址时使用此种办法。所有私有地址IP对应全局分配地址IP的一个端口,私有地址IP通过该端口与外部网络通讯

 


TCP是一种流协议 它没有明显边界 两个包的发送可能是下面多种情况

解决办法是

1 每次读取固定字节

函数如下

int readn(SOCKET fd, char* bp, size_t len) {
	int cnt;
	int rc;

	cnt = len;
	while (cnt > 0) {
		rc = recv(fd, bp, cnt, 0);
		if (rc < 0) {
			return -1;
		}
		if (rc == 0) {
			return len - cnt;
		}
		bp += rc;
		cnt -= rc;
	}

	return len;
}

2 在报文前面添加首部 说明报文的长度

函数如下

int readvrec(SOCKET fd, char* bp, size_t len) {
	uint32_t reclen;
	int rc;
	rc = readn(fd, (char *)&reclen, sizeof(uint32_t));
	if (rc != sizeof(uint32_t))
		return rc < 0 ? -1 : 0;
	reclen = ntohl(reclen);
	//如果发送内容大于BUF长度 收取发送内容后丢弃
	if (reclen > len) {
		while (reclen > 0) {
			rc = readn(fd, bp, len);
			if (rc != len)
				return rc < 0 ? -1 : 0;
			reclen -= len;
			if (reclen < len)
				len = reclen;
		}
		return -2;
	}

	//正常情况
	rc = readn(fd, bp, reclen);
	if (rc != reclen)
		return rc < 0 ? -1 : 0;
	return rc;
}


全部代码如下

通用头文件

#ifndef __UNP_H__
#define __UNP_H__

#include <WinSock2.h>  
#include <iostream>
#include <WS2tcpip.h>
#pragma comment(lib,"WS2_32.lib")  

#define	MAXLINE	1024


void err_sys(const char* msg) {
	std::cout << msg << std::endl;
	exit(-1);
}

class InitWinSock{
public:
	InitWinSock(){
		WORD myVersionRequest;
		WSADATA wsaData;
		myVersionRequest = MAKEWORD(2,2);
		int err = WSAStartup(myVersionRequest,&wsaData);
		if(err){
			exit(-1);
		}
	}
	~InitWinSock(){
		WSACleanup();
	}	
};





#endif //#ifndef __UNP_H__

客户端

#include "../common/unp.h"

#define DEFAULT_IP_ADDR	"127.0.0.1"

InitWinSock initsock;

struct {
	uint32_t reclen;
	char buf[128];
}packet;

int main()
{
	int sockfd;
	int n;
	struct sockaddr_in servaddr;

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		err_sys("sock error");
	}

	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(7500);
	if (inet_pton(AF_INET, DEFAULT_IP_ADDR, &servaddr.sin_addr) <= 0)
		err_sys("inet_pton error");

	/*printf("inet_pton: 0x%x\n",  servaddr.sin_addr);*/
	if (connect(sockfd, (sockaddr*)&servaddr, sizeof(servaddr)) < 0)
		err_sys("connect error");

	while (fgets(packet.buf, sizeof(packet.buf), stdin) != NULL){
		n = strlen(packet.buf);
		packet.reclen = htonl(n);
		if (send(sockfd, (char*)&packet, n + sizeof(packet.reclen), 0) < 0) {
			err_sys("send fail\n");
		}
	}


    return 0;
}


服务端
#include "../common/unp.h"

InitWinSock initsock;

int readn(SOCKET fd, char* bp, size_t len) {
	int cnt;
	int rc;

	cnt = len;
	while (cnt > 0) {
		rc = recv(fd, bp, cnt, 0);
		if (rc < 0) {
			return -1;
		}
		if (rc == 0) {
			return len - cnt;
		}
		bp += rc;
		cnt -= rc;
	}

	return len;
}

int readvrec(SOCKET fd, char* bp, size_t len) {
	uint32_t reclen;
	int rc;
	rc = readn(fd, (char *)&reclen, sizeof(uint32_t));
	if (rc != sizeof(uint32_t))
		return rc < 0 ? -1 : 0;
	reclen = ntohl(reclen);
	//如果发送内容大于BUF长度 收取发送内容后丢弃
	if (reclen > len) {
		while (reclen > 0) {
			rc = readn(fd, bp, len);
			if (rc != len)
				return rc < 0 ? -1 : 0;
			reclen -= len;
			if (reclen < len)
				len = reclen;
		}
		return -2;
	}

	//正常情况
	rc = readn(fd, bp, reclen);
	if (rc != reclen)
		return rc < 0 ? -1 : 0;
	return rc;
}



int main()
{
	struct sockaddr_in	local;
	int s;
	int s1;
	int rc;
	char buf[10];
	struct sockaddr_in peer;
	int peerlen = sizeof(peer);

	local.sin_family = AF_INET;
	local.sin_port = htons(7500);
	local.sin_addr.s_addr = htonl(INADDR_ANY);

	s = socket(AF_INET, SOCK_STREAM, 0);
	if (s < 0) {
		err_sys("socket call failed");
	}

	rc = bind(s, (struct sockaddr*)&local, sizeof(local));
	if (rc < 0) {
		err_sys("bind call failure");
	}

	rc = listen(s, 5);
	if (rc) {
		err_sys("listen call failed");
	}

	s1 = accept(s,(struct sockaddr*)&peer,&peerlen);
	if (s1 < 0) {
		err_sys("accept error");
	}
	for (;;) {
		int n = readvrec(s1, buf, sizeof(buf));
		if (n < 0) {
			if(n == -2)
				printf("readvrec buf to short \n");
			else {
				err_sys("readvrec error\n");
			}
		}
		else if (n == 0) {
			err_sys("client disconnected \n");
		}
		else {
			if (n > 9)
				n = 9;
			buf[n] = '\0';
			printf("%s",buf);
		}
	}


    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值