socket编程流程总结

11 篇文章 7 订阅
6 篇文章 1 订阅

TCP与UDP的区别

基于连接与无连接
  对系统资源的要求(TCP较多,UDP少)
  UDP程序结构较简单
  流模式与数据报模式
  TCP保证数据正确性,UDP可能丢包
  TCP保证数据顺序,UDP不保证
  部分满足以下几点要求时,应该采用UDP 面向数据报方式 网络数据大多为短消息
  拥有大量Client
  对数据安全性无特殊要求
  网络负担非常重,但对响应速度要求高
  具体编程时的区别 socket()的参数不同
  UDP Server不需要调用listen和accept
  UDP收发数据用sendto/recvfrom函数
  TCP:地址信息在connect/accept时确定
  UDP:在sendto/recvfrom函数中每次均 需指定地址信息
  UDP:shutdown函数无效

man----socket

通过查看socket的man手册可以看到socket函数的第一个参数的值可以为下面这些值:
  Name Purpose
  PF_UNIX, PF_LOCAL Local communication
  PF_INET IPv4 Internet protocols
  PF_INET6 IPv6 Internet protocols
  PF_IPX IPX - Novell protocols
  PF_NETLINK Kernel user interface device
  PF_X25 ITU-T X.25 / ISO-8208 protocol
  PF_AX25 Amateur radio AX.25 protocol
  PF_ATMPVC Access to raw ATM PVCs
  PF_APPLETALK Appletalk
  PF_PACKET Low level packet interface

编程区别

通常我们在说到网络编程时默认是指TCP编程,即用前面提到的socket函数创建一个socket用于TCP通讯,函数参数我们通常填为SOCK_STREAM。即socket(PF_INET, SOCK_STREAM, 0),这表示建立一个socket用于流式网络通讯。
  SOCK_STREAM这种的特点是面向连接的,即每次收发数据之前必须通过connect建立连接,也是双向的,即任何一方都可以收发数据,协议本身提供了一些保障机制保证它是可靠的、有序的,即每个包按照发送的顺序到达接收方。

而SOCK_DGRAM这种是User Datagram Protocol协议的网络通讯,它是无连接的,不可靠的,因为通讯双方发送数据后不知道对方是否已经收到数据,是否正常收到数据。任何一方建立一个socket以后就可以用sendto发送数据,也可以用recvfrom接收数据。根本不关心对方是否存在,是否发送了数据。它的特点是通讯速度比较快。大家都知道TCP是要经过三次握手的,而UDP没有。

基于上述不同,UDP和TCP编程步骤也有些不同,如下:
  TCP编程的服务器端一般步骤是:
  1、创建一个socket,用函数socket();
  2、设置socket属性,用函数setsockopt(); * 可选
  3、绑定IP地址、端口等信息到socket上,用函数bind();
  4、开启监听,用函数listen();
  5、接收客户端上来的连接,用函数accept();
  6、收发数据,用函数send()和recv(),或者read()和write();
  7、关闭网络连接;
  8、关闭监听;

TCP编程的客户端一般步骤是:
  1、创建一个socket,用函数socket();
  2、设置socket属性,用函数setsockopt();* 可选
  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
  4、设置要连接的对方的IP地址和端口等属性;
  5、连接服务器,用函数connect();
  6、收发数据,用函数send()和recv(),或者read()和write();
  7、关闭网络连接;

在这里插入图片描述

与之对应的UDP编程步骤要简单许多,分别如下:
  UDP编程的服务器端一般步骤是:
  1、创建一个socket,用函数socket();
  2、设置socket属性,用函数setsockopt();* 可选
  3、绑定IP地址、端口等信息到socket上,用函数bind();
  4、循环接收数据,用函数recvfrom();
  5、关闭网络连接;

UDP编程的客户端一般步骤是:
  1、创建一个socket,用函数socket();
  2、设置socket属性,用函数setsockopt();* 可选
  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
  4、设置对方的IP地址和端口等属性;
  5、发送数据,用函数sendto();
  6、关闭网络连接

一.TCP流式套接字的编程步骤

在使用之前须链接库函数:工程->设置->Link->输入ws2_32.lib

服务器端程序

流程

1、加载套接字库
2、创建套接字(socket)。
3、将套接字绑定到一个本地地址和端口上(bind)。
4、将套接字设为监听模式,准备接收客户请求(listen)。
5、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。
6、用返回的套接字和客户端进行通信(send/recv)。
7、返回,等待另一客户请求。
8、关闭套接字。

服务器端代码

#include <Winsock2.h>//加裁头文件
#include <stdio.h>//加载标准输入输出头文件
void main()
{
	WORD wVersionRequested;//版本号
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD( 1, 1 );//1.1版本的套接字
	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) { return; }//加载套接字库,加裁失败则返回
	if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 ){
		WSACleanup( );
		return;
	}//如果不是1.1的则退出
	SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//创建套接字(socket)。
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
	//转换Unsigned short为网络字节序的格式 
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(6000);
	bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); 
	//将套接字绑定到一个本地地址和端口上(bind) listen(sockSrv,5);
	//将套接字设为监听模式,准备接收客户请求(listen)。
	SOCKADDR_IN addrClient;//定义地址族
	int len=sizeof(SOCKADDR);//初始化这个参数,这个参数必须被初始化
	while(1){
	SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
	//accept的第三个参数一定要有初始值。
	//等待客户请求到来;当请求到来后,接受连接请求,
	//返回一个新的对应于此次连接的套接字(accept)。 
	//此时程序在此发生阻塞
	char sendBuf[100];
	sprintf(sendBuf,"Welcome %s to www.msn.com.cn",inet_ntoa(addrClient.sin_addr)); 
	//用返回的套接字和客户端进行通信(send/recv)。
	send(sockConn,sendBuf,strlen(sendBuf)+1,0);
	char recvBuf[100];
	recv(sockConn,recvBuf,100,0);
	printf("%s\n",recvBuf);
	closesocket(sockConn);//关闭套接字。等待另一个用户请求
	}
}

客户端程序

流程

1、加载套接字库
2、创建套接字(socket)。
3、向服务器发出连接请求(connect)。
4、和服务器端进行通信(send/recv)。
5、关闭套接字。

客户端代码

#include <Winsock2.h>
#include <stdio.h>

void main(){
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD( 1, 1 );
	err = WSAStartup( wVersionRequested, &wsaData );//加载套接字库
	if ( err != 0 ) { return; }
	if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ){
	WSACleanup( );
	return;
	}
	SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);//创建套接字(socket)。
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(6000);
	connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
	//向服务器发出连接请求(connect)。
	char recvBuf[100];//和服务器端进行通信(send/recv)。
	recv(sockClient,recvBuf,100,0);
	printf("%s\n",recvBuf);
	send(sockClient,"This is blues_j",strlen("This is blues_j")+1,0);
	closesocket(sockClient);//关闭套接字。
	WSACleanup();//必须调用这个函数清除参数
}

二.UDP型套接字的编程步骤

服务器(接收)端程序

1、创建套接字(socket)。
2、将套接字绑定到一个本地地址和端口上(bind)。
3、等待接收数据(recvfrom)。
4、关闭套接字。

服务器端代码

#include <Winsock2.h>
#include <stdio.h>
void main(){
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD( 1, 1 );
	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) { return; }
	if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 ){
	WSACleanup( );
	return;
	}
	SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(6000);
	bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
	SOCKADDR_IN addrClient;
	int len=sizeof(SOCKADDR);
	char recvBuf[100];
	recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);
	printf("%s\n",recvBuf);
	closesocket(sockSrv); WSACleanup();
}

客户端(发送)端程序

客户端(发送端)程序:
1、创建套接字(socket)。
2、向服务器发送数据(sendto)。
3、关闭套接字。

客户端端代码

#include <Winsock2.h>
#include <stdio.h>
void main(){
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD( 1, 1 );
	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) { return; }
	if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 ){
	WSACleanup( );
	return;
	}
	SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(6000);
	sendto(sockClient,"Hello",strlen("Hello")+1,0, (SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
	closesocket(sockClient);
	WSACleanup();
}

三.聊天程序中的注意事项

聊天程序常用的是UDP式套接字。因为TCP的三步握手开销比较大。
socket通讯

网络字节序

小端法: 高位存高地址,低位存低地址。 (intel架构的存储方式)
大端法:高位存低地址,低位存高地址。(网络传输的方式)

#include <arpa/inet.h>

// 将本地转网络,转IP 转192.168.1.11->string->atoi->int->htonl->网络字节序,可以使用
int inet_pton(); 进行直接转换

uint32_t  htonl(uint32_t hostlong); 

// 本地转网络,转port

uint16_t  htons(uint16_t hostshort);

// 网络转本地,转ip

uint32_t ntohl(uint32_t netlong);

// 网络转本地,转port

uint16_t ntohs(uint16_t netshort);

// string转网络字节

int inet_pton(int af, const char * restrict src, void * restrict dst);

// af: AF_INET, AF_INET6
// src: ip地址,点分十进制
// dst: 转换之后的 网络字节序的地址

创建socket服务器的步骤

创建socket句柄
bind() 绑定ip+port
listen() 设置监听上线,同时连接数
accept() 阻塞监听客户端连接
read() 进行数据的读取,读取到的数据需要toupper()进行小写转大写
write() 写入,写给客户端返回值
当read()读到0就是close的时候
进行 close() 关闭

Socket函数分析

创建一个套接字

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

domain : 所选用的ip地址协议, AF_INET, AF_INET6
type : 类型 SOCK_STREAM(TCP/流形式), SOCK_DGRAM(UDP/报形式)
protocol : 代表协议号 0
返回值:

成功返回0,新套接字的文件描述

失败返回: -1
#include <sys/socket.h>

fd = socket(AF_INET, SOCK_STREAM, 0)

sockaddr地址结构体分析

#include <sys/socket.h>
#include <arpa/inet.h>

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9527);
int dst;
inet_pton(AF_INET, "192.168.22.45", (void *)&dst);
addr.sin_addr.s_addr = dst;
addr.sin_addr.s_addr = htonl(INADDR_ANY) 

INADDR_ANY 取出系统有效的任意ip地址,是二进制类型

bind(fd, (struct sockaddr *)&addr, size);

bind函数的分析

绑定函数

bind(fd, (struct sockaddr *)&addr, size);

fd : socket文件对象
(struct sockaddr *)&addr : 将sockaddr存储的地址结构进行强制转换成sockaddr 进行传入
size : addr的大小,使用sizeof进行获取

listen函数分析

监听函数

int listen(int sockfd, int backlog);

sockfd : 套接字
backlog : 最大连接数,最大为128
返回值: 0, -1 error

accept函数分析

堵塞函数

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd : socket 函数返回值
addr : 传出参数, 成功返回服务器的ip和端口号
addrlen : 传入传出。入: addr的大小。 出:客户端addr的实际大小
返回值:

成功:大于0,返回成功的套接字文件描述符
失败:返回-1

connect函数分析

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 

使用现有的socket与服务器建立连接

sockfd: socket 函数返回值
addr: 传入服务器的地址结构
返回值:
成功:0
失败:-1

小Tips(以后专门整理)

  1. WM_APP:
    想要定义其自己消息的应用程序应该使用WM_APP。WM_APP 是确保不会与系统(WM_CREATE 等等)或类/特定控件消息如DM_GETDEFID 相冲突的。
  2. WSACleanup:
    每次调用WSAStartup,都要调用相应的WSACleanup;因为每次启动调都会增加加载winsock DLL的引用计数,因此必须调同样多的WSACleanup,以减少引用计数
  3. Winsock API:
    在“传输层”和“会话层”之间。
  4. TCP/UDP:
    TCP:可靠连接。字节流,无报文边界。
    UDP:无确认,不可靠,无连接。有报文边界。
  5. 端口:
    0~1023:系统保留;
    1024~65535:用户使用;
  6. 同时打开的套接字数目:不固定的,与可用的物理内存有关。
    7.一般受到SOCKET_ERROR后,除了WSAEWOULDBLOCK, 都应该关闭套接字,因为它不能再用了。
  7. recv可以“消息取数”,即偷看。但不好,性能下降(2次系统调用)。
  8. recv:把数据留在参数缓冲中不好,那样剩余缓冲量将减少,可能使系统减少发送端TCP窗口容量,从而使网络流量减少。所以最好把接收到的数据复制到自己的buffer中。
  9. send: TCP流情况下,发出的字节也许少于理想的字节。解决方法:用一个循环发送。
  10. TCP流情况下,10个send, 也许被1-2个recv就全接收了。
  11. UDP: 有connect, 可使用send;
    无connect, 只可使用sendto
  12. WSAEWOULDBLOCK: 很多情况下,都可能出现。
  13. GetLastError: 独立与线程的,线程之间各自有各自的,不影响!!!
  14. MailSlot: 客户机到服务器,不可靠,单向。垃圾!
  15. 消息长了,尽量考虑面向连接,因为无连接分片,丢一个就白折腾了
  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Socket C是一种在计算机网络通信中使用的编程接口,它允许应用程序通过网络进行数据传输和通信。在C语言中,我们可以使用一些函数来进行Socket编程。 引用中的代码展示了如何创建一个服务器端的Socket,并检查是否创建成功。首先,我们使用socket函数创建一个Socket,指定了地址族(AF_INET)、套接字类型(SOCK_STREAM)和协议类型(IPPROTO_TCP)。如果创建成功,返回的socket描述符将被赋给serverSocket变量。如果创建失败,我们将打印出Socket错误信息。 引用中的代码展示了如何接受客户端的连接请求。首先,我们声明了一个用于存储客户端地址信息的结构体client_sin,并定义了一个变量len来存储该结构体的大小。然后,我们使用accept函数来接受客户端的连接,并将连接的socket描述符赋给clientSocket变量。同时,我们也获取了客户端的地址信息,并存储在client_sin中。 引用中的描述展示了使用Socket的一般流程。首先,我们需要创建一个Socket,并绑定到指定的IP地址和端口。然后,我们使用listen函数来监听该Socket,以接受客户端的连接请求。接下来,我们可以使用accept函数来接受连接,并获取连接的socket描述符。在通信过程中,我们可以使用recv函数来接收消息的内容。通信完成后,我们使用closeSocket函数来关闭与连接对应的socket。 在Socket编程中,如果不需要等待任何客户端的连接,我们可以直接使用closeSocket函数来关闭自身的socket。 总结起来,Socket C是一种在计算机网络通信中使用的编程接口,通过一些函数来创建、连接、接受和关闭Socket,实现数据传输和通信的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C++学习之Socket](https://blog.csdn.net/weixin_42299076/article/details/124828768)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值