Socket网络编程

Socket网络编程

Qfan

Qfan

公众号(Qfan)分享技术、职场、人生、以及毕业后的点点滴滴

​关注他

10 人赞同了该文章

Linux Socket编程(不限Linux)

Linux下的C++ socket编程实例

Socket实战之一--快速入门

 

套接字概念

Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。

既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。与管道类似的,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。

套接字的内核实现较为复杂,不宜在学习初期深入学习。

在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。
套接字通信原理如下图所示:


套接字通讯原理示意
在网络通信中,套接字一定是成对出现的。一端的发送缓冲区对应对端的接收缓冲区。我们使用同一个文件描述符索发送缓冲区和接收缓冲区。
TCP/IP协议最早在BSD UNIX上实现,为TCP/IP协议设计的应用层编程接口称为socket API。现在的主要内容是socket API,主要介绍TCP协议的函数接口,最后介绍UDP协议和UNIX Domain Socket的函数接口。
 


网络编程接口

1、什么是计算机网络?
多个计算机进行通信--->计算机网络。
2、计算机通信的复杂度
(1)、传输信息的复杂度(种类、内容);
(2)、信息的数量
(3)、传输距离(干扰...)
(4)、信息的安全问题
(5)、计算机体系的完整性和封闭性。
既要保证计算机的封闭性,又要达成计算机的通信。
3、ip地址
(1)、IP地址是有限的,需要一种方式将IP地址复用。
(2)、IP地址的复用导致了数据传递的复杂性(ATM,存储转发机制;路由机制)。
(3)、IP地址过于抽象不方便使用,于是给出了IP地址的人文化转义:域名。
(4)、域名只能代表一个IP网络地址,于是就只能代表一个网络上的节点实体。
(5)、实际上访问节点的时候,本质上使用的是IP地址,所以就需要将域名转化为IP地址(DNS...)
4、IP地址的分类
(1)、IPv4地址是4字节的,中间以 . 划分;IPv6地址是16字节的;

规定:在IP地址划分上,一般不取全0/全1;

IP一般分为5类;
A、B、C、D、E,一般常用的IP地址为A类,B类,C类;


A类IP:第一个字节是以0开头 0000 0000--->0111 1111 0~127

B类IP:第一个字节是以10开头 1000 0000--->1011 1111 128~191 

C类IP:第一个字节是以110开头 1100 0000--->1101 1111 192~223
(2)、子网掩码:就是将网络号设置为1,主机号设置为0(对每一个字节的位进行设置)
例:C类IP地址,3个字节网络号和一个字节的主机号;
1111 1111 1111 1111 1111 1111 0000 0000
255 . 255 . 255 . 0
(3)、子网划分:此时就存在C类地址的子网掩码不一定总是255.255.255.0;
这还的看C类IP下面有没有子网划分,
有子网划分的话,最后一个字节,也就是主机号可能为2段(01/10)、4段(00/01/10/11)
例:192.168.3.11xx xxxx 1111 1111 1111 1111 1111 1111 1100 0000

此时对应的子网掩码为:255.255.255.192
(4)、IOS和TCP/IP
模型分析


(5)、端口号
port:唯一标识应用程序的编号;
我们之间通过QQ、微信、邮箱进行收发数据时,没有导致数据的错乱接收,是怎么做到的呢?又是怎么一一对应找到的呢?
:通过端口号,识别了电脑上的某一应用程序,也就是找对应的编号。
我们在进行数据的发送时:首先通过IP寻找物理计算机,在根据port,寻找对应的应用程序。
(6)、TCP和UDP
UDP属于TCP/IP体系中的一部分。
TCP协议和UDP协议都属于传输层协议。
TCP协议:
i>、面向连接的传输协议、可靠的、同步的;
ii>、面向连接的网络传输特点:a、需要有一方主动的建立连接,另一方接收连接请求;b、只有建立了连接之后才能够进行数据的传输;c、当数据传输完毕之后,就需要释放连接,由连接的两端来共同决定连接是否保持。
UDP协议:
a、面向无连接的:即就是在进行数据传输的时候,不需要预先创建一个连接;
b、不可靠的:无法知道发送的数据是否能够到达目的,也无法知道什么时候能够到达目的。
c、异步的:
(7)、TCP的三次握手、四次挥手
TCP------->至少3次握手(最后一次防止误按,2次的话,有可能死锁); : 打电话模型

模型分析


TCP-------->4次挥手。 模型:男女朋友分手模型


通过IP,只能保证物理上的连通,至于收发数据的形式是什么,都不归它管。
127.0.0.1:本机回送地址,可作为测试本机使用,不安装网卡也是可以ping通的。
5、TCP的编程实现
基础的socket编程对TCP的就是下面的步骤:

(1)、模型分析


(2)、代码实现
utili.h
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define SERVER_PORT 9090
#define SERVER_IP "127.0.0.1"
#define LISTEN_QUEUE 5
#define BUF_SIZE 255

服务器端代码:
#include"utili.h"//TCP
int main(void)
{
int sockSer = socket(AF_INET, SOCK_STREAM, 0); 
if(sockSer == -1){
perror("socket");
return -1; 


struct sockaddr_in addrSer, addrCli;
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(SERVER_PORT);
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);

int yes = 1;
setsockopt(sockSer, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); //地址、端口的重用

socklen_t len = sizeof(struct sockaddr);
int res = bind(sockSer, (struct sockaddr *)&addrSer, len);
if(res == -1)
{
perror("bind");
close(sockSer);
return -1; 


res = listen(sockSer, LISTEN_QUEUE);
if(res == -1)
{
perror("listen");
close(sockSer);
return -1;
}

int sockConn;
char sendbuf[BUF_SIZE];
char recvbuf[BUF_SIZE];
while(1)
{
sockConn = accept(sockSer, (struct sockaddr *)&addrCli, &len);
if(sockConn == -1){
continue;
}
else
{
printf("Server Accept Client Connect OK\n");
}
printf("Ser :>");
scanf("%s", sendbuf);
if(strncmp(sendbuf, "quit", 4) == 0)
break;
send(sockConn, sendbuf, strlen(sendbuf)+1, 0);

recv(sockConn, recvbuf, BUF_SIZE, 0);
printf("Cli :>%s\n", recvbuf); 
}
close(sockSer);

return 0;}

客户端代码:
#include"utili.h"//TCP
int main(void)
{
int sockCli = socket(AF_INET, SOCK_STREAM, 0); 

struct sockaddr_in addrSer;
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(SERVER_PORT);
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);

struct sockaddr_in addrCli;
addrCli.sin_family = AF_INET;
addrCli.sin_port = htons(7070);
addrCli.sin_addr.s_addr = inet_addr("192.168.1.155");

int yes = 1;
setsockopt(sockCli, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); //地址、端口的重用

socklen_t len = sizeof(struct sockaddr);

int res = bind(sockCli, (struct sockaddr *)&addrCli, len);
if(res == -1)
{
perror("bind");
close(sockCli);
return -1; 


res = connect(sockCli, (struct sockaddr*)&addrSer, len);
if(res == -1)
{
perror("connect");
close(sockCli);
return -1;
}
else
{
printf("Client Connect Server ok\n");
}

char sendbuf[BUF_SIZE];
char recvbuf[BUF_SIZE];
while(1)
{
connect(sockCli, (struct sockaddr*)&addrSer, len);
recv(sockCli, recvbuf, BUF_SIZE, 0);
printf("Ser :>%s\n", recvbuf);

printf("Cli :>");
scanf("%s", sendbuf);
if(strncmp(sendbuf, "quit", 4) == 0) 
break;
send(sockCli, sendbuf, strlen(sendbuf)+1, 0);

}
close(sockCli);

return 0;}

(3)、运行结果

 



6、UDP的编程实现
基础的socket编程对UDP的就是下面的步骤:
(1)、模型分析


(2)、代码实现
utili.h
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define SERVER_PORT 9090
#define SERVER_IP "127.0.0.1"
#define LISTEN_QUEUE 5
#define BUFFER_SIZE 255

服务器端代码:
#include"utili.h"
int main()
{
int sockSer = socket(AF_INET, SOCK_DGRAM, 0); 
if(sockSer == -1)
{
perror("socket");
return -1; 

struct sockaddr_in addrSer, addrCli;
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(SERVER_PORT);
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);

socklen_t len = sizeof(struct sockaddr);
int res = bind(sockSer, (struct sockaddr*)&addrSer, len);
if(res == -1)
{
perror("bind");
close(sockSer);
return -1; 


char sendbuf[BUFFER_SIZE];
char recvbuf[BUFFER_SIZE];
while(1)
{
recvfrom(sockSer, recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&addrCli, &len);
printf("Cli:>%s\n",recvbuf);

printf("Ser:>");
scanf("%s",sendbuf);
if(strncmp(sendbuf, "quit", 4) == 0){
break;
}
sendto(sockSer, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr*)&addrCli, len);
}
close(sockSer);
return 0;}

客户端代码:
#include"utili.h"
int main()
{
int sockCli = socket(AF_INET, SOCK_DGRAM, 0); 
if(sockCli == -1){
perror("socket");
return -1; 


struct sockaddr_in addrSer;
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(SERVER_PORT);
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);

char sendbuf[BUFFER_SIZE];
char recvbuf[BUFFER_SIZE];
socklen_t len = sizeof(struct sockaddr);
while(1){
printf("Cli:>");
scanf("%s",sendbuf);
if(strncmp(sendbuf, "quit", 4) == 0){ 
break;

sendto(sockCli, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr*)&addrSer, len);

recvfrom(sockCli, recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&addrSer, &len);
printf("Ser:>%s\n",recvbuf);
}
close(sockCli);
return 0; 
}

(3)、运行结果
服务器端截图


客户端截图


服务端的套接字总领全局,不与任何客户端进行通信,为每一个新的客户端所分配一个新的套接字,进行通信。
LISTEN_QUEUE:等待队列的大小(最多等待多少队列);
UDP:必须先知道服务器在哪。


喜欢本文的朋友们,欢迎长按下图关注订阅号编程小兔崽,收看更多精彩内容

http://weixin.qq.com/r/NByrsyHEkXzurWXQ90km (二维码自动识别)

一个简单的socket网络编程例子: 服务器代码: #include #include #include #include #pragma comment(lib,"ws2_32.lib") //这句话的意思是加载ws2_32.lib这个静态库 #define NETWORK_EVENT WM_USER+100 //如果你用mfc做开发,你可以点击菜单project-〉setting-〉link-〉object/library中添加这个静态库。 //如果你用c语言,你需要通过#pragma comment(命令来连接静态库 int main(int argc, char* argv[]){ HANDLE hThread = NULL; //判断是否输入了端口号 if(argc!=3){ printf("Usage: %sPortNumber\n",argv[1]); exit(-1); } //把端口号转化成整数 short port; if((port = atoi(argv[2]))==0){ printf("端口号有误!"); exit(-1); } WSADATA wsa; //初始化套接字DLL if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){ //高字节指定了次版本号,低字节指定了主版本号,两个字节加到一起,就是你想要的Winsock库的版本号了 printf("套接字初始化失败!"); exit(-1); } //创建套接字 SOCKET serverSocket; if((serverSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){ printf("创建套接字失败!"); exit(-1); } struct sockaddr_in serverAddress; memset(&serverAddress,0,sizeof(sockaddr_in)); serverAddress.sin_family=AF_INET; serverAddress.sin_addr.S_un.S_addr = htonl(INADDR_ANY); serverAddress.sin_port = htons(port); //绑定 if(bind(serverSocket,(sockaddr*)&serverAddress,sizeof(serverAddress))==SOCKET_ERROR){ printf("套接字绑定到端口失败!端口: %d\n",port); exit(-1); } //进入侦听状态 if(listen(serverSocket,SOMAXCONN)==SOCKET_ERROR){ printf("侦听失败!"); exit(-1); } printf("Server %d is listening......\n",port); SOCKET clientSocket[5],maxSocket;//用来和客户端通信的套接字 struct sockaddr_in clientAddress;//用来和客户端通信的套接字地址 memset(&clientAddress,0,sizeof(clientAddress)); int addrlen = sizeof(clientAddress); fd_set fd_read; int i=0; int j; char buf[4096]; char buff[4096]="exit"; while(1) { FD_ZERO(&fd_read); maxSocket=serverSocket; FD_SET(serverSocket,&fd_read); //FD_SET(clientSocket[i-1],&fd_read); for(j=0;j<i;j++) { FD_SET(clientSocket[j],&fd_read); if(maxSocket"); //gets(buff); if(select(maxSocket+1,&fd_read,NULL,NULL,NULL)>0) { if(FD_ISSET(serverSocket,&fd_read)) { if(buff=="") { if((clientSocket[i++]=accept(serverSocket,(sockaddr*)&clientAddress,&addrlen))==INVALID_SOCKET) { printf("接受客户端连接失败!"); exit(-1); } else { for(j=0;j5) { printf("超过最大客户端数"); exit(-1); } } else { int bytes; for(int k=0;k<i;k++) { if(FD_ISSET(clientSocket[k],&fd_read)) { bytes=recv(clientSocket[k],buf,sizeof(buf),0); if(bytes==-1) { //listen(serverSocket,SOMAXCONN); for (int l=k;l<i;l++) clientSocket[l]=clientSocket[l+1]; i--; } /*if(bytes==0) { //printf("fdsdf"); listen(serverSocket,SOMAXCONN); for (int l=k;l0) { buf[bytes]='\0'; printf("Message from %s: %s\n",inet_ntoa(clientAddress.sin_addr),buf); if(send(clientSocket[k],buf,bytes,0)==SOCKET_ERROR) { printf("发送数据失败!"); exit(-1); } } } } } } } //清理套接字占用的资源 WSACleanup(); return 0; } 客户端代码: #include #include #include #pragma comment(lib,"ws2_32.lib") int main(int argc, char* argv[]){ //判断是否输入了IP地址和端口号 if(argc!=4){ printf("Usage: %s IPAddress PortNumber\n",argv[1]); exit(-1); } //把字符串的IP地址转化为u_long unsigned long ip; if((ip=inet_addr(argv[2]))==INADDR_NONE){ printf("不合法的IP地址:%s",argv[1]); exit(-1); } //把端口号转化成整数 short port; if((port = atoi(argv[3]))==0){ printf("端口号有误!"); exit(-1); } printf("Connecting to %s:%d......\n",inet_ntoa(*(in_addr*)&ip),port); WSADATA wsa; //初始化套接字DLL if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){ printf("套接字初始化失败!"); exit(-1); } //创建套接字 SOCKET sock,serverSocket; if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){ printf("创建套接字失败!"); exit(-1); } struct sockaddr_in serverAddress; memset(&serverAddress,0,sizeof(sockaddr_in)); serverAddress.sin_family=AF_INET; serverAddress.sin_addr.S_un.S_addr = ip; serverAddress.sin_port = htons(port); //建立和服务器的连接 if(connect(sock,(sockaddr*)&serverAddress,sizeof(serverAddress))==SOCKET_ERROR) { printf("建立连接失败!"); exit(-1); } char buf[4096]; while(1){ printf(">"); //从控制台读取一行数据 gets(buf); if(send(sock,buf,strlen(buf),0)==SOCKET_ERROR){ printf("发送c数据失败!"); exit(-1); } int bytes; if((bytes=recv(sock,buf,sizeof(buf),0))==SOCKET_ERROR) { printf("接收c数据失败!\n"); exit(-1); } else { buf[bytes]='\0'; printf("Message from %s: %s\n",inet_ntoa(serverAddress.sin_addr),buf); } } //清理套接字占用的资源 WSACleanup(); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值