基于TCP的Socket 编程

 

基于TCP(面向连接)的socket编程,分为客户端和服务器端。

客户端的流程如下:

(1)创建套接字(socket)

(2)向服务器发出连接请求(connect)

(3)和服务器端进行通信(send/recv)

(4)关闭套接字

服务器端的流程如下:

(1)创建套接字(socket)

(2)将套接字绑定到一个本地地址和端口上(bind)

(3)将套接字设为监听模式,准备接收客户端请求(listen)

(4)等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)

(5)用返回的套接字和客户端进行通信(send/recv)

(6)返回,等待另一个客户请求。

(7)关闭套接字。

 

下面通过一个具体例子讲解一下具体的过程和相关的函数。

客户端代码,运行于vs2008

// ClientTest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>

#define SERVER_PORT 5208 //侦听端口


int _tmain(int argc, _TCHAR* argv[])
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int ret;
	SOCKET sClient; //连接套接字
	struct sockaddr_in saServer; //服务器地址信息
	char *ptr;
	BOOL fSuccess = TRUE;

	//WinSock初始化
	wVersionRequested = MAKEWORD(2, 2); //希望使用的WinSock DLL的版本
	ret = WSAStartup(wVersionRequested, &wsaData);	//加载套接字库
	if(ret!=0)
	{
		printf("WSAStartup() failed!\n");
		//return 0;
	}
	//确认WinSock DLL支持版本2.2
	if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)
	{
		WSACleanup();	//释放为该程序分配的资源,终止对winsock动态库的使用
		printf("Invalid WinSock version!\n");
		//return 0;
	}

	//创建Socket,使用TCP协议
	sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sClient == INVALID_SOCKET)
	{
		WSACleanup();
		printf("socket() failed!\n");
		//return 0;
	}

	//构建服务器地址信息
	saServer.sin_family = AF_INET; //地址家族
	saServer.sin_port = htons(SERVER_PORT); //注意转化为网络节序
	saServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	//连接服务器
	ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer));
	if (ret == SOCKET_ERROR)
	{
		printf("connect() failed!\n");
		closesocket(sClient); //关闭套接字
		WSACleanup();
		//return 0;
	}


	char sendMessage[]="ZhongXingPengYue"; 
	ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), 0);
	if (ret == SOCKET_ERROR)
	{
		printf("send() failed!\n");
	}
	else
		printf("client info has been sent!");
	char recvBuf[100];
	recv(sClient,recvBuf,100,0);
	printf("%s\n",recvBuf);
	closesocket(sClient); //关闭套接字
	WSACleanup();
	getchar();
	//return 0;
}

 

        第一步,加载套接字。使用WSAStartup 函数,如:ret = WSAStartup(wVersionRequested, &wsaData)。WSAStartup函数的原型为

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData)

第一参数wVersionRequested,用来指定准备加载的winsock库的版本。利用MAKEWORD(x,y)宏来赋值。x是高位字节,表示副版本号;y是低位字节,表示主版本号。MAKEWORD(2, 2)表示版本号为2.2。

第二个参数是指向WSADATA结构的指针,是一个返回值,保存了库版本的有关信息。

 

       第二步,创建套接字。使用socket函数,如:sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)。socket函数的原型为:

SOCKET socket(int af, int type, int protocol );

第一个参数,指定地址族,对于TCP/IP协议的套接字,只能为AF_INET;

第二个参数,指定socket类型,SOCK_STREAM指产生流式套接字,SOCK_DGRAM指产生数据报套接字,TCP/IP协议使用SOCK_STREAM。

第三个参数,与特定的地址家族相关的协议,TCP协议一般为IPPROTO_TCP。也可以写0,那么系统会根据地址格式和套接字类别,自动选择一个适合的协议。

如果socket创建成功,则返回一个新的SOCKET数据类型的套接字描述符;若失败,则返回INVALID_SOCKET,由此可以判断是否创建成功。

 

        第三步,连接服务器。使用connect函数,如:ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer))。connect函数函数原型为

int connect(SOCKET s, const struct sockaddr FAR* name, int namelen);

第一个参数是将在上面建立连接的那个套接字的描述符,即之前创建socket的返回值sClient。

第二个参数是要连接的服务器端的地址信息。它是一个结构体类型struct sockaddr_in ,需要在调用connect函数之前构建服务器地址信息。sockaddr_in的定义如下:

struct sockaddr_in{

short sin_family;

unsigned short sin_port;

struct in_addr sin_addr;

char sin_zero[8]

};


设置服务器端口时,用到htons函数,该函数把一个u_short类型的值从主机字节顺序转换为TCP/IP网络字节顺序,因为不同的计算机存放多字节的顺序不同(基于Intel CPU是高字节存放在低地址,低字节存放在高地址),所以网络中不同主机间通信时,要统一采用网络字节顺序。设置服务器IP地址时,使用到inet_addr函数,它是将点分十进制的IP地址的字符串转换成unsigned long型。inet_ntoa函数做相反的转换。

第三个参数是服务器端地址结构体的大小。

 

        第四步,发送。使用send函数向服务器发送数据,如:ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), 0)。send函数的原型为

int send(SOCKET s, const char FAR* buf, int len, int flags);

第一个参数,是一个与服务器已经建立连接的套接字。

第二个参数,指向包含要发送的数据的缓冲区的指针。

第三个参数,是所指向的缓冲区的长度。准确的说,应该是所要发送的数据的长度,因为不是缓冲区的所有数据都要同时发送。

第四个参数,它设定的值将影响函数的行为,一般将其设置为0即可。

如果发送失败,send会返回SOCKET_ERROR,由此可以判断发送是否成功。

 

        第五步,接收。使用recv函数接收服务器发过来的数据,如recv(sClient,recvBuf,100,0)。recv函数的原型为

int recv(SOCKET s, const char FAR* buf, int len, int flags);

recv函数的参数的含义和send函数参数含义差不多,只是第二个参数是指向用来保存接收数据的缓冲区的指针。recv函数的返回值应该是所接收的数据的长度,如果返回SOCKET_ERROR表示接收失败;返回0表示服务器端关闭连接。

 

       第六步,关闭socket,释放资源。使用closesocket函数关闭套接字,如closesocket(sClient);使用WSACleanup函数释放为该程序分配的资源,终止对winsock动态库的使用,如WSACleanup();
 

服务器端代码,运行于vs2008

// ServerTest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>

#define SERVER_PORT 5208 //侦听端口

int _tmain(int argc, _TCHAR* argv[])
{
	WORD wVersionRequested;
    WSADATA wsaData;
    int ret, nLeft, length;
    SOCKET sListen, sServer; //侦听套接字,连接套接字
    struct sockaddr_in saServer, saClient; //地址信息   
    char *ptr;//用于遍历信息的指针   
    //WinSock初始化
    wVersionRequested=MAKEWORD(2, 2); //希望使用的WinSock DLL 的版本
    ret=WSAStartup(wVersionRequested, &wsaData);
    if(ret!=0)
    {
        printf("WSAStartup() failed!\n");
        //return 0;
    }
    //创建Socket,使用TCP协议
    sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sListen == INVALID_SOCKET)
    {
        WSACleanup();
        printf("socket() faild!\n");
        //return 0;
    }
    //构建本地地址信息
    saServer.sin_family = AF_INET; //地址家族
    saServer.sin_port = htons(SERVER_PORT); //注意转化为网络字节序
    saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址
  
    //绑定
    ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer));
    if (ret == SOCKET_ERROR)
    {
        printf("bind() faild! code:%d\n", WSAGetLastError());
        closesocket(sListen); //关闭套接字
        WSACleanup();
        //return 0;
    }
  
    //侦听连接请求
    ret = listen(sListen, 5);
    if (ret == SOCKET_ERROR)
    {
        printf("listen() faild! code:%d\n", WSAGetLastError());
        closesocket(sListen); //关闭套接字
        //return 0;
    }
  
    printf("Waiting for client connecting!\n");
    printf("Tips: Ctrl+c to quit!\n");
    //阻塞等待接受客户端连接
	while(1)//循环监听客户端,永远不停止,所以,在本项目中,我们没有心跳包。
	{
		length = sizeof(saClient);
		sServer = accept(sListen, (struct sockaddr *)&saClient, &length);
		if (sServer == INVALID_SOCKET)
		{
			printf("accept() faild! code:%d\n", WSAGetLastError());
			closesocket(sListen); //关闭套接字
			WSACleanup();
			return 0;
		}  

		char sendMessage[]="hello client";  //发送信息给客户端
		send(sServer,sendMessage,strlen(sendMessage)+1,0);

		char receiveMessage[5000];
		nLeft = sizeof(receiveMessage);
		ptr = (char *)&receiveMessage;
		while(nLeft>0)
		{
			//接收数据
			ret = recv(sServer, ptr, 5000, 0);
			if (ret == SOCKET_ERROR)
			{
				printf("recv() failed!\n");
				return 0;
			}
			if (ret == 0) //客户端已经关闭连接
			{
				printf("Client has closed the connection\n");
				break;
			}
			nLeft -= ret;
			ptr += ret;
		}  
		printf("receive message:%s\n", receiveMessage);//打印我们接收到的消息。
		

	} 
  //  closesocket(sListen);
  //  closesocket(sServer);
  //  WSACleanup();
	return 0;
}


 

        第一步,加载套接字库,和客户端得加载套接字一样。

 

        第二步,创建监听套接字,sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);仍然使用的是socket函数。

 

        第三步,绑定。使用bind函数,该函数的作用是将一个创建好的套接字绑定到本地的某个地址和端口上,该函数的原型为:

int bind(SOCKET s, const struct sockaddr FAR* name, int namelen);

第一个参数,指定要绑定的套接字;

第二个参数,指定该套接字的地址信息,这里即服务器的地址信息,它仍是指向struct sockaddr_in类型的结构体的指针。这个结构体和客户端调用connect函数之前构建服务器地址信息的一样。其中INADDR_ANY 是指示任意地址,因为服务器含有可能多个网卡,可能有多个IP地址,这边指选择一个任意可用的地址。

第三个参数,地址的信息的长度。

 

        第四步,监听连接。使用listen函数,该函数是将指定的套接字设置为监听模式,如ret = listen(sListen, 5)。函数原型为

int listen(SOCKET s, int backlog);


第一个参数,是要设置为监听的套接字描述符。

第二个参数,是等待连接队列的最大的长度。注意了,设置这个值是为了设置等待连接队列的最大长度,而不是在一个端口上可以连接的最大数目。例如,设置为5,当有6个连接请求同时到达,前面5个连接请求会被放到等待请求连接队列中,然后服务器依次处理这些请求服务,但是第6个连接请求会被拒绝。

 

       第五步,接受客户端的连接请求。使用accept函数接受客户端发送的连接请求,如sServer = accept(sListen, (struct sockaddr *)&saClient, &length);该函数的原型为

SOCKET accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen);


第一个参数,是一个已设为监听模式的socket的描述符。

第二个参数,是一个返回值,它指向一个struct sockaddr类型的结构体的变量,保存了发起连接的客户端得IP地址信息和端口信息。

第三个参数,也是一个返回值,指向整型的变量,保存了返回的地址信息的长度。

accept函数返回值是一个客户端和服务器连接的SOCKET类型的描述符,在服务器端标识着这个客户端。

      

       第六、七步是发送和接收,分别使用send和recv函数,这里和客户端的一样,不再重复。

       accept函数是放在一个死循环中的,一直监听客户的请求。当服务器关闭时要关闭所有的套接字,和释放资源。

 

 

  • 11
    点赞
  • 99
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: Python基于TCPSocket编程是一种网络编程技术,它使用TCP协议来实现网络通信。通过Python的Socket模块,我们可以创建一个TCP服务器或客户端,实现数据的传输和接收。在这种编程模式下,服务器和客户端之间通过套接字(socket)建立连接,然后通过发送和接收数据来进行通信。Python基于TCPSocket编程可以用于各种网络应用程序,如Web服务器、邮件服务器、聊天室等。 ### 回答2: Python基于TCPsocket编程是一种实现网络通信的方式。通过使用Python中的socket模块,可以创建一个Socket对象,并将其与本地主机上的IP地址和端口号绑定,以实现网络接口的监听。在此之后,该Socket对象接受网络数据包请求,并将其作为一组字节发送到特定的网络地址上。无论是服务器还是客户端,都可以使用此方法建立连接并发送数据。 Python中的TCP socket编程需要双方进行协议的定义,包括请求协议、响应协议和数据传输协议。这些协议可以自定义,也可以使用标准的TCP/IP协议。 在编写socket程序时,可以根据需要选择阻塞或非阻塞模式。如果使用阻塞模式,程序将等待其他Socket对象的响应,然后才能继续执行下去;如果使用非阻塞模式,程序将立即返回,并且在未收到响应时将执行其他操作。 Python TCP socket编程的一个重要方面是如何处理异常情况。在网络通信中,可能发生各种各样的异常,例如连接断开、数据损坏等。对于这些异常,程序应该对其进行处理,以便能够及时诊断和修复错误。 Python TCP socket编程还可以与其他网络通信技术结合使用,例如HTTP、FTP等。通过这种方式,可以实现更复杂的网络应用程序,例如Web服务器、FTP服务器等。 在Python TCP socket编程中,需要使用一些Python语言的特性,例如面向对象编程、异常处理、多线程等。掌握这些技巧,可以使程序更加稳定、高效、安全,并且易于调试和维护。 ### 回答3: Python可以通过TCP协议实现基于Socket的网络通信。Socket是一种网络通信协议,它可以通过Internet或本地网络实现不同计算机之间的通信。Socket包括两个物理端口,一个用于发送数据,另一个用于接收数据。 Python使用socket模块来实现网络编程。在基于TCPSocket编程中,需要使用socket对象来创建客户端和服务器,其中客户端使用connect()函数与服务器进行连接,而服务器端使用bind()和listen()函数进行绑定和监听。服务器在接收到客户端连接请求之后,使用accept()函数接受请求并返回一个新的Socket对象,该对象用于与客户端进行通信。 Python中通过Socket模块的一些常见方法进行socket编程,例如: 1. socket.socket(): 创建一个新的socket对象。 2. socket.bind(address): 绑定IP地址和端口号,创建一个服务器。 3. socket.listen(backlog): 开始监听,并设置最大连接数。 4. socket.accept(): 接受客户端的连接请求,并返回新的socket对象。 5. socket.connect(address): 连接到服务器。 6. socket.send(data): 向socket发送数据。 7. socket.recv(buffer_size): 从socket接收数据。 8. socket.close(): 关闭socket连接。 使用Python实现socket编程时需要注意: 1. 使用TCP协议时需要先连接服务器,然后再发送或接收数据。 2. 在服务器端,需要在while循环中不断接收客户端的请求,并对请求进行处理。 3. 在客户端与服务器连接之后,需要及时关闭socket连接,避免资源浪费。 以上是Python基于TCPSocket编程的基本方法和注意事项。通过socket编程,可以实现网络通信等功能,广泛应用于客户端与服务器之间的通信、文件传输、远程控制等领域。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值