摘录笔记 《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;
}