进一步的,推荐可以去看https://blog.csdn.net/lg1259156776/article/details/50816858
TCP,UDP,IP之间的区别:可以去看https://blog.csdn.net/tjcwt2011/article/details/78323379
参考过别人写的代码:https://blog.csdn.net/oinux/article/details/8525823
https://blog.csdn.net/shenjie12345678/article/details/28321969#commentsedit
TCP,UDP的多线程的实现:可以去看https://blog.csdn.net/qinghe0808/article/details/74599575
1.套接字
使用端口号和网络地址的组合可以唯一地确定整个网络中的一个网络进程
一个完整的套接字的描述为{协议,本地地址,本地端口,远程地址,远程端口}来表示
2.Socket类型
(1)流式Socket(SPCKET_STREAM):用于TCP
(2)数据报Socket(SOCKET_DGRAM):用于UDP
(3)yuan原始Socket(SOCKET_RAW):用于新的网络协议实现的测试
3.Socket信息数据结构
sa_family:AF_INET表示IPv4协议,AF_INET6表示IPv6协议
4.数据存储优先顺序的转换
操作系统采用的字节序为主机字节序。
计算机数据存储有两种字节优先顺序:高位字节优先(大段模式),低位字节优先(小端模式)。
内存的低地址存储数据的低字节(高字节),高地址存储数据的高字节(低字节)称为:小端(大端);
端口号和IP地址都是以网络字节存储的,而不是主机字节序,网络字节序都是大端模式。
5.地址格式转化
具体函数介绍:
(1)
(2)
(3)
6.名字地址转化
将www.baidu.comzh这个域名转化为IP地址
在linuxzho中,最常见的是gethostbyname()、gethostbyaddr()等,实现IPv4和IPv6的地址和zhu主机名之间的转化,
其中gethostbyname()是jian将主机名转化为IP地址,gethostbyaddr()是将IP地址转化为主机名。
例子:下面的代码是用装在虚拟机的linux写的!
7.重头戏:网络编程
使用TCP的C/S架构
使用UDP的C/S架构
步骤:(1)Socket函数用于建立一个Socket通信
(2)bind函数用于对Socket定位
(3)listen函数用于等待监听
(4)accept函数用于接受Socket连线
(5)connect函数用于建立Socket连线
(6)send函数用于通过Socket传送数据,sendto函数用于通过Socket传送数据
(7)recv函数用于通过Socket接收数据,recvfrom函数用于通过Socket接收数据
8.为了解决一个客户访问时会阻塞其它客户的访问,为了解决该阻塞模式,采用了并发服务器模型。
并发服务器模型的实现主要有三种方法:
(1)多进程:开销较大,一般不用
(2)多线程:将服务器上的内容全部发给客户端
(3)调用fcntl将sockfd设置为非阻塞模式
9.多路转接模型:服务器+客户端
10.在编写UDP的通信实验过程中,注:
(1)
if(n):
如果n为boolean类型,则判断n是否为true,如果是true执行if后语句,否则执行else后语句;
如果n为整型,则判断n是否为0,如果是0执行else后语句,否则执行if后语句;
如果n为字符类型,则判断字符n对应的ASCII码值是否为0,若果是执行else后语句,否则执行if后语句;
(2)C语言中return 0和return 1和return -1
返回值int 类型的函数返回:
return语句用来结束循环,或返回一个函数的值。
return 0:一般用在主函数结束时,按照程序开发的一般惯例,表示成功完成本函数。
return -1::表示返回一个代数值,一般用在子函数结尾。按照程序开发的一般惯例,表示该函数失败;
以上两个是约定俗成,系统提供的函数绝大部分定义为int类型返回值的都是这样的。返回值是返回给系统用的,给系统看得。一般做调试的时候也会用的,当出现错误的时候可以根据返回值来确定问题出在哪一个函数上的。
再次提醒,注意此时返回的类型是int。
return 0:返回假;
return 1:返回真;
一般这样的函数用来实现一个判断是否的逻辑,或检查有无的数据。返回真表示“是”,返回假表示“否”!如:isalpha()判断是否是字母 isdigit()判断是否是数字。
c语言编译系统在给出逻辑运算结果时,以“1”表示真,以“0”表示假。例如:i = 1 > 3;则i的值为0。反之,i = 1 < 3;则i的值为1。
(3)用本机socket编写服务器和客户端,并在本机上测试的时候,服务器的ip地址和端口号要怎么写
本机你可以写成 "127.0.0.1"或者"localhost",如果是安卓本机就是"10.0.2.2"。端口随便你写,只要系统不冲突就行。
10.UDP的实现,用VS2010的控制台实现,client发一个数据,server不仅会输出,还会发送反馈给client,测试已经完全通过
上面socket函数库编写过程还参考了《高质量的linux c编程》,里面还有使用linux方法编写的UDP和TCP。
client端:
/*
单线程编写,使用UDP
参考的函数是linux 下的Socket编程函数,Windows下是大同小异
Client端口:
socket:建立套接字
bind:发布端口(netstat-an可见已经发布的端口)
sendto/recv/recvfrom:收发数据
*/
#define _CRT_SECURE_NO_WAENINGS
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")//编译器设置——链接库
using namespace std;
const int PORT=8888;
int main()
{
/*
Win sockek需要多准备的工作:编译库文件和头文件不同,在开始运行
的时候,需要指定socket的版本,结束后需要清理
*/
int n;
WSADATA wd;
n=WSAStartup(MAKEWORD(2,2),&wd);//选择socket的版本
if(n)
{
printf("WSAStartup错误");
return -1;
}
//(1)建立socket
SOCKET sock=socket(AF_INET,SOCK_DGRAM ,0);
//(2)绑定地址
SOCKADDR_IN udp_addr;//typedef....
memset(&udp_addr,0,sizeof(struct sockaddr));//将结构体清空
udp_addr.sin_family=AF_INET;//采用IPv4协议
udp_addr.sin_port=htons(PORT);//htons()用来将参数指定的16位hostshort转换成网络字符顺序
udp_addr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//指定要发往的IP地址和端口号
//(3)收发数据
char sendData[256];
int len=sizeof(struct sockaddr);
char recvData[255]={'\0'}; //一定要初始化,要不然出现“烫”字眼
//char * sendData = "来自客户端的数据包.\n";
//printf(sendData);
while(1)
{
gets(sendData);
sendto(sock, sendData, strlen(sendData), 0, (sockaddr *)&udp_addr, len);
recvfrom(sock, recvData, sizeof(recvData), 0, (struct sockaddr *)&udp_addr, &len);
printf("client接收:%s\n",recvData);
}
closesocket(sock);
WSACleanup();
system("pause");
return(0);
}
server端口:
/*
单线程编写,使用UDP
参考的函数是linux 下的Socket编程函数,Windows下是大同小异
Server端口:
socket:建立套接字
bind:发布端口(netstat-an可见已经发布的端口)
sendto/recv/recvfrom:收发数据
*/
#define _CRT_SECURE_NO_WAENINGS
#include<stdio.h>
#include<iostream>
#include<string>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")//编译器设置——链接库
using namespace std;
const int PORT=8888;
int main()
{
/*
Win sockek需要多准备的工作:编译库文件和头文件不同,在开始运行
的时候,需要指定socket的版本,结束后需要清理
*/
int n;
WSADATA wd;
n=WSAStartup(MAKEWORD(2,2),&wd);//选择socket的版本
if(n)
{
printf("WSAStartup错误");
return -1;
}
//(1)建立socket
SOCKET sock=socket(AF_INET,SOCK_DGRAM ,0);//socket函数,参考int socket()
//(2)绑定地址
SOCKADDR_IN udp_addr;//typedef....
udp_addr.sin_family=AF_INET;//采用IPv4协议
udp_addr.sin_port=htons(PORT);//htons()用来将参数指定的16位hostshort转换成网络字符顺序
udp_addr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//指定要发往的IP地址和端口号
int m=bind(sock,(struct sockaddr *)&udp_addr,sizeof(struct sockaddr));
if(SOCKET_ERROR==m)
{
printf("bind错误: %d", WSAGetLastError());
return -1;
}
//接收数据
SOCKADDR_IN remoteAddr;
int Len = sizeof(struct sockaddr);
while (1)
{
//收发数据
char recvData[255]={'\0'}; //一定要初始化,要不然出现“烫”字眼
recvfrom(sock, recvData, sizeof(recvData), 0, (struct sockaddr *)&remoteAddr, &Len);
printf("接收到的数据为:%s\n",recvData);
printf("接受到一个连接:[%s] \r\n", inet_ntoa(remoteAddr.sin_addr));
char sendData[256]={"server发送的数据包"};
if(strcmp(recvData,"wj")==0)//若字符串是一样的,则return 0
strcat(sendData,"a");
sendto(sock, sendData, strlen(sendData), 0, (struct sockaddr *)&remoteAddr, Len);
}
/*
//char buf[]="this is server";
char buf[256]={'\0'};
int len=sizeof(struct sockaddr);
recvfrom(sock, buf, sizeof(buf), 0,(struct sockaddr*)&udp_addr,(int *)&len);
//recv(sock, recbuf, sizeof(recbuf),0);
printf("server接收到的是: %s\n",buf);
*/
closesocket(sock);
WSACleanup();
system("pause");
return 0;
}
11.TCP的实现,编写方法还是一样,使用VS2010控制台实现(经过调试,是可以使用的,实现的功能是client与server之间的交互)
server端:
#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib, "ws2_32.lib")
const int port=5012;
int main()
{
WSADATA wsdata;
int n=WSAStartup(MAKEWORD(2,2),&wsdata);
if(n==1)
{
printf("Failed to load Winsock");
return -1;
}
SOCKADDR_IN addrsrv;
addrsrv.sin_family = AF_INET;
addrsrv.sin_port = htons(port);//1024以上的端口号
addrsrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//创建套接字
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);//TCP要选择SOCK_STREAM,UDP如果选择这个,会出现一堆JB错误
if(SOCKET_ERROR == sock)
{
printf("Socket() error:%d", WSAGetLastError());
return -1;
}
int retVal = bind(sock, (struct sockaddr *)&addrsrv, sizeof(struct sockaddr));
if(retVal == SOCKET_ERROR)
{
printf("Failed bind:%d\n", WSAGetLastError());
return -1;
}
//监听
if(listen(sock,15) ==SOCKET_ERROR)
{
printf("Listen failed:%d", WSAGetLastError());
return -1;
}
sockaddr_in remoteAddr;
int len = sizeof(struct sockaddr);
char buf[] = "Server: hello, I am a server.....";
while(1)
{
//等待客户请求到来
SOCKET sockConn = accept(sock, ( struct sockaddr *) &addrsrv, &len);
if(sockConn == SOCKET_ERROR){
printf("Accept failed:%d", WSAGetLastError());
break;
}
printf("Accept client IP:[%s]\n", inet_ntoa(addrsrv.sin_addr));
while(1)
{
//发送数据
int iSend = send(sockConn, buf, sizeof(buf) , 0);
if(iSend == SOCKET_ERROR)
{
printf("send failed");
break;
}
char recvBuf[100];
memset(recvBuf, 0, sizeof(recvBuf));
//接收数据
recv(sockConn, recvBuf, sizeof(recvBuf), 0);
printf("%s\n", recvBuf);
}
closesocket(sockConn);
}
closesocket(sock);
WSACleanup();
system("pause");
return 0;
}
client端
#include <WinSock2.h>
#include <stdio.h>
#include<stdlib.h>
#include<iostream>
#pragma comment(lib, "ws2_32.lib")
const int port=5012;
int main()
{
//加载套接字
WSADATA wsaData;
char buff[1024]={"wangj"};
memset(buff, 0, sizeof(buff));
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Failed to load Winsock");
return -1;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(port);
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//创建套接字
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
if(SOCKET_ERROR == sockClient)
{
printf("Socket() error:%d", WSAGetLastError());
return -1;
}
//向服务器发出连接请求
if(connect(sockClient, (struct sockaddr*)&addrSrv, sizeof(addrSrv)) == INVALID_SOCKET)
{
printf("Connect failed:%d", WSAGetLastError());
return -1;
}
//发送数据
char str[256]={'\0'};
while(1)
{
gets(str);
send(sockClient, str, sizeof(buff), 0);
//接收数据
recv(sockClient, buff, sizeof(buff), 0);
printf("%s\n", buff);
}
//char buff[1024] = "hello, this is a Client....";
//关闭套接字
closesocket(sockClient);
WSACleanup();
system("pause");
//return 0;
}