socket网络编程概述

本文介绍了套接字的三种类型:流式套接字(SOCK_STREAM)基于TCP,提供可靠的数据传输;数据报式套接字(SOCK_DGRAM)基于UDP,提供无连接服务;原始套接字(SOCK_RAW)。接着详细讲解了TCP和UDP的socket编程,包括服务器端和客户端的程序流程,以及TCP与UDP的区别和一个基于UDP的简单聊天程序示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.套接字的类型

1.流式套接字(SOCK_STREAM)

        基于TCP协议实现的。

        提供面向连接、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接受。

2.数据报式套接字(SOCK_DGRAM)

        基于UDP协议。

        提供无连接服务。数据包以独立包形式发送,不提供无错保证,数据可能重复或丢失,并且接受顺序混乱。

3.原始套接字(SOCK_RAM)

二.基于TCP(面向连接)的socket编程

1.服务器端的程序流程

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

总结如下:create socket -> bind -> listen -> accept -> send/recv -> close

2.客户端的程序流程

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

总结如下:create socket -> connect -> send/recv -> close

3.一个基于TCP的socket通信的例子

(1).服务器端(server)

        使用2个socket:一个sockSrv,用来监听客户请求,等待客户请求的到来;另一个是sockConn,用来和客户端通信的(发送/接受)。

#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")

void main()
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		/* Tell the user that we could not find a usable */
		/* WinSock DLL.                                  */
		return;
	}

	/* Confirm that the WinSock DLL supports 2.2.*/
	/* Note that if the DLL supports versions greater    */
	/* than 2.2 in addition to 2.2, it will still return */
	/* 2.2 in wVersion since that is the version we      */
	/* requested.                                        */

	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			/* Tell the user that we could not find a usable */
			/* WinSock DLL.                                  */
			WSACleanup( );
			return; 
	}

	/* The WinSock DLL is acceptable. Proceed. */

	//create a socket to listen
	SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,0);
	SOCKADDR_IN addrSrv;
	//INADDR_ANY表示IP为0.0.0.0,表示所在机器的所有地址,因为对于多网卡的机器
	//会有多个IP,那么用0.0.0.0进行socket的bind,则所有网卡都可监听到
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(7777);	

	//bind the socket
	bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
	//set the socket to be listening and prepare to receive the client request
	listen(sockSrv , 5 );

	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);

	while (1)
	{
	  //wait client request
	  SOCKET sockConn = accept(sockSrv,(SOCKADDR*)&addrClient,&len);
	  char sendBuf[100]= "welcome 11";
	  sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(addrClient.sin_addr));
	  //send data
	  send(sockConn,sendBuf,strlen(sendBuf)+1,0);
	  char recvBuf[100];
	  //receive data
	  recv(sockConn,recvBuf,100,0);
	  printf("%s \n",recvBuf);
	  closesocket(sockConn);
	}
	getchar();
	return;
}
 

(2).客户端(client)

       只用一个socket:sockClient,进行通信。

#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib") 

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;
	}

	//create a socket
	SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//服务器IP
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(7777);

	//send a connect request
	connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

	//receive data
	char recvBuf[100];
	recv(sockClient,recvBuf,100,0);
	printf("%s \n",recvBuf);

	//send data
	send(sockClient,"welcome zhaolianxiang",strlen("welcome zhaolianxiang")+1,0);

	//close socket
	closesocket(sockClient);
	WSACleanup();
	getchar();
}



三.基于UDP(面向无连接)的socket编程

       基于UDP的socket编程中,其服务器端和客户端的概念不是很强化。可以把先启动的一端称为接收端(服务器端),发送数据的一端成为发送端(客户端)。

1.服务器端(接收端)的程序流程

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

总结:create socket -> bind ->recvfrom ->close。

2.客户端(发送端)的程序流程

  1. 创建套接字(socket)。
  2. 向服务器发送数据(sendto)。
  3. 关闭套接字(close)。

总结:create socket ->sendto ->close。

3.一个基于UDP的socket通信的例子

(1).接收端(服务器)

        只用一个socket:sockSrv进行通信。

#include <Winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")

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();
}

(2).发送端(客户端)

       只用一个socket:sockClient进行通信。

#include <Winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")

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");//接收方的IP
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(6000);

	sendto(sockClient,"Hello",strlen("Hello")+1,0,
		(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//发送数据
	closesocket(sockClient);  //关闭套接字
	WSACleanup();
}

四.TCP socket和UDP socket的比较

       TCP socket模式:C/S模式,即客户端/服务器端。适用于一个server,多个client,即一对多。使用send/recv函数发送和接受数据。

       UDP socket模式:发送端/接收端模式,即先启动的一端为接收端。适用于一对一的情况,比如聊天。即client <----> client。使用sendto/recvfrom函数发送和接受数据。

五.一个基于UDP的简单聊天程序

(1).服务器端

#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")

void main()
{
	WORD wVersionRequested;
	WSAData wsaData;
	int err;
	wVersionRequested = MAKEWORD(2,2);
	err = WSAStartup(wVersionRequested,&wsaData);
	if (err != 0)
	{
		return;
	}
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		return;
	}

	//create a socket
	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(5555);
	
	//bind the socket
	bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

	char recvBuf[1000];
	char sendBuf[1000];
	char tempBuf[1000];

	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);
	while (1)
	{
		//receive data
		recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);
		if ('q' == recvBuf[0])
		{
			sendto(sockSrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClient,len);
			printf("chat end!\n");
			break;
		}
		sprintf(tempBuf,"%s say: %s",inet_ntoa(addrClient.sin_addr),recvBuf);
		printf("%s \n",tempBuf);

		//send data
		printf("pls input data:\n");
		gets(sendBuf);
		sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClient,len);
	}
	closesocket(sockSrv);
	WSACleanup();
	getchar();
};

分析:服务器端(接收端)是先接受数据,再发送数据。只有一个socket对象用来通信:sockSrv,。

(2).客户端

#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")

void main()
{
	WORD wVersionRequested;
	WSAData wsaData;
	int err;
	wVersionRequested = MAKEWORD(2,2);
	err = WSAStartup(wVersionRequested,&wsaData);
	if (err != 0)
	{
		return;
	}
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		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(5555);

	int len = sizeof(SOCKADDR);
	char recvBuf[100];
	char sendBuf[100];
	char tempBuf[100];

	while (1)
	{
		printf("pls input data:\n");
		gets(sendBuf);
		sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)(&addrSrv),len);

		recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrSrv,&len);
		if ('q' == recvBuf[0])
		{
			sendto(sockClient,"q",strlen("q")+1,0,(SOCKADDR*)&addrSrv,len);
			printf("chat end!\n");
                        break;
		}
		sprintf(tempBuf,"%s says: %s",inet_ntoa(addrSrv.sin_addr),recvBuf);
		printf("%s \n",tempBuf);
	}
	closesocket(sockClient);
	WSACleanup();
	getchar();
};
分析:客户端(发送端)是先发送数据,后接受数据。只用一个socket:sockClient进行通信。


 
 
 
 

                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值