TCP网络套接字C语言

1.什么是TCP网络套接字

       TCP(Transmission Control Protocol)是一种常用的网络通信协议,用于在互联网上可靠地传输数据。网络套接字(Socket)是TCP通信的一种机制,它提供了在不同计算机之间进行数据交换的方式。

可以这样理解套接字:想象你和你的朋友在不同的城市,你们需要通过电话进行交流。那么,你的手机就可以看作是一个套接字,它提供了与手机网络的接口,使你能够与你的朋友通话。

类似地,在网络中,计算机之间通过套接字进行通信。每个套接字都有一个唯一的地址,称为IP地址,用于标识计算机的位置。而套接字中的端口号则用于标识计算机上的不同应用程序。

举个例子,假设你是一名客户,想要通过网络购买一本书。你的计算机可以充当客户端,向网络上的图书商家发起连接。这时,你的客户端会创建一个套接字,并指定目标图书商家的IP地址和端口号。一旦连接建立起来,你的客户端就可以向图书商家发送购买请求,并接收返回的书籍信息。

在这个例子中,套接字提供了一个有效的通信机制,使得你的计算机能够与图书商家的计算机进行可靠的数据传输,确保购买过程的正确性和完整性。这就是TCP网络套接字的作用。

2.什么是端口号

端口号是用于标识计算机上不同应用程序的数字。类似于现实生活中的门牌号码,它可以告诉我们在一个建筑物中每个房间的具体位置。

假设你的计算机就像一座大楼,而不同的应用程序就像楼里的不同房间。每个房间都有一个唯一的门牌号码,用于标识该房间。同样地,每个应用程序都会监听一个特定的端口号,以便其他计算机通过网络找到它并与之通信。

例如,Web服务器一般使用端口号80。当你在浏览器中输入一个网址时,浏览器会通过网络连接到目标Web服务器的IP地址,并指定端口号80。这样,Web服务器就知道将接收的请求交给哪个应用程序来处理,并将网页内容返回给你的浏览器。

又比如,邮件服务器一般使用端口号25或465。当你发送或接收电子邮件时,电子邮件客户端会通过网络连接到目标邮件服务器的IP地址,并指定相应的端口号。这样,邮件服务器就知道将邮件传递给哪个应用程序,或者从哪个应用程序接收邮件。

总之,端口号就像是应用程序的标识符,它使得不同的应用程序能够在同一台计算机上同时监听网络连接,并通过网络进行数据交换。通过使用不同的端口号,我们可以在计算机做多个事情,每一个端口号标志着做一件事情,这件事情可以是邮件服务,浏览网址,或者通信等等事情,每一件事情都有一个对应的号码,就是端口号。

3.什么是网络字节序列

数值例如0x1122使用两个字节储存:高位字节是0x11,低位字节是0x22。

大端字节序:高位字节在前,低位字节在后,这是人类读写数值的方法。

小端字节序:低位字节在前,高位字节在后,即以0x2211形式储存。

4.TCP套接字的socket编程

TCP是面向连接、可靠的传输协议,利用TCP协议进行通信时,首先要建立通信双方的连接,一旦连接建立成功,就可以进行通信了。

4.1 基于TCP面向连接的socket编程的服务端流程

1.初始化套接字库
2.创建套接字
3.绑定本机地址
4.设置套接字的监听状态
5.接收连接请求
6.开始收、发数据
7.通信完毕,释放套接字资源

4.2 基于TCP面向连接的socket编程的客户端流程

1.初始化套接字库
2.创建套接字
3.向服务端发起连接请求
4.请求连接成功,就可以相互收、发信息进行通信
5.释放套接字资源

5.套接字函数介绍

5.1 WSAStartup()函数

函数原型: int WSAStortup(WORD wVersionRequested LPWSAADTA lpWSAate)
函数功能:初始化套接字库
第一个参数: 使用Windows socket的版本 例如:Windows socket 2.1版本 高字节是1,低字节是2第二个参数:指向WSADATA结构体指针

typedef struct WSAData {
				WORD       wVersion;            // winsock库的版本号
				WORD       wHighVersion;        // winsock库支持的最高版本号
				char       szDescription[WSADESCRIPTION_LEN+1];  // 描述信息
				char       szSystemStatus[WSASYS_STATUS_LEN+1];   // 系统状态信息
				unsigned short iMaxSockets;      // 可以打开的最大套接字数
				unsigned short iMaxUdpDg;        // UDP数据报的最大大小
				char FAR*  lpVendorInfo;         // 供应商信息
} WSADATA;

函数返回值:成功返回0,失败返回非零

检测版本号举例
例如:
WORD w_req = MAKEWORD(2.1) 

	MAKEWORD宏定义奖版本号转换成网络字节顺序,有大段模式和小端模式两种
	大端模式:高位字节存储低位地址,低位字节存储高位地址
	小端模式:低位字节存储低位地址,高位字节存储高位地址
	
   WSADATA wsa_data;
   WSAStartup(w_req, &wsa_data); 这样wsa_data结构体第一个参数就存储了版本号 
   
检测版本号 (高字节存储的是主版本,低字节存储的是副版本)
if (LOBYTE(wsa_data.wVersion) != 1 || HIBYTE(wsa_data.wVersion) != 2) {  提示错误信息。}

5.2 socket()函数

函数原型: socket(int af, int type, int protocol)
函数功能:创建一个套接字。
第一个参数:表示地址族,通常为AF_INET,表示IPV4
第二个参数:表示套接字类型

有三种类型套接字,面向连接的流式套接字(SOCK_STREAM)、面向无连接的数据包套接字(SOCK_DGRAM)、原始套接字(SOCK_RAW)

第三个参数:表示套接字所使用的协议,如果用户不指定,可以设置0,0表示默认的是TCP协议

返回值:当创建成功就返回一个新的SOCKET数据类型的套接字,失败则返回-1

创建套接字举例
SOCKET s_server;
s_server = socket(AF_INET, SOCK_STREAM, 0);

5.3 bind()函数

函数原型: bind(SOCKET s, const struct sockaddr FAR my_addr, int len)
函数功能:将套接字绑定到本地的ip地址和端口上。
第一个参数创建的套接字

当使用了函数socket()成功建立,就会返回一个SOCKET类型的套接字,此时将返回的套接字给到bind()函数第一个参数位置

第二个参数是一个sockaddr结构体

struct sockaddr {
				unsigned short sa_family;     地址簇
				char sa_data[14];             地址的具体信息
};

第三个参数是结构体的大小
返回值:绑定成功返回0,失败返回-1

5.3 使用bind()函数考虑的事情

使用bind()函数需要做4件事情
1.定义结构体SOCKADDR_IN类型结构体变量

struct sockaddr_in {
			short int sin_family;            地址簇,通常为AF_INET
			unsigned short int sin_port;     端口号
			struct in_addr sin_addr;         IPv4地址
			unsigned char sin_zero[8];       填充字段,通常为0
};
其中第三个成员又是一个结构体
typedef struct in_addr {
        union {
                struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
                struct { USHORT s_w1,s_w2; } S_un_w;
                ULONG S_addr;
        } S_un;
}

2.结构体成员sin_family设置AF_INET,其中AF_INET表示IPv4协议
3.设置端口号 sin_port
4.设置服务端绑定的本机地址,这里一般设置INADDR_ANY,表示任意可用的的地址,当客户端向指定的ip地址发送连接请求,此时的服务端会监控当前的ip地址是否可用。

bind()函数应用举例
SOCKADDR_IN sin;
sin.sinfamily = AFINET;  设置地址族 IPV4协议
sin.sin_port = htons(8888); 设置端口号 htons将整型转换成网络字节顺序
sin.sin_addr.s_addr = htonl(INADDR_ANY); 

设置地址,其中INADDR_ANY表示任意可用的的地址,
htonl将无符号长整型转换成网络字节序列

int bind_return = bind(s_server, (SOCKADDR*) &sin, sizeof(SOCKADDR));
if (bind_return < 0) {
	printf("套接字绑定失败!\n");
	WSACleanup();
	return 0;
}		

需要注意的是由于bind函数第二个参数的类型是sockaddr 结构体指针类型,该结构体只有两个成员
struct sockaddr {
				unsigned short sa_family;     地址簇
				char sa_data[14];             地址的具体信息
};
但是传入的sin是结构体SOCKADDR_IN,该结构体有4个成员
struct sockaddr_in {
			short int sin_family;            地址簇,通常为AF_INET
			unsigned short int sin_port;     端口号
			struct in_addr sin_addr;         IPv4地址
			unsigned char sin_zero[8];       填充字段,通常为0
};
sockaddr_in结构体是sockaddr结构体的拓展功能,且sockaddr_in结构体与sockaddr结构体大小相同
都是16字节大小,从而bind函数第二个参数强制类型转换成sockaddr_in就不会造成数据丢失情况,
由于bind第二个参数是sockaddr指针类型,所以第二个参数的写法(SOCKADDR*) &sin,
先将sin转换成指针,sin是一个结构体变量,前面加了一个&,变成sin变量的地址,地址也是指针,
然后前面加SOCKADDR*,将指针sockaddr_in* 类型强制类型转换成指针sockaddr* 类型。

5.4 listen()函数

函数原型 int listen(SOCKET sockfd, int backlog)
函数功能:将套接字设置监听模式,对于流式套接字,必须设置监听模式才能接收客户端套接字发来的连接请求。

第一个参数:套接字

当使用了函数socket()成功建立,就会返回一个SOCKET类型的套接字,此时将返回的套接字给到listen()函数第一个参数位置

第二个参数:表示等待连接的最大队列长度。

当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能吧新的请求放进缓冲区,等待当前请求处理完成后,再从缓冲区读取出来处理,如果不断的有新的请求进来,它们就按照顺序在缓冲区排队,直到缓冲区存满,这个缓冲区就是等待队列,当等待队列满之后,再有新的请求进来,就会被丢弃。例如backlog长度设置10,此时有15个客户端同时发出连接请求,那么前10个客户端连接请求会存放到等待队列,后5个客户端连接请求会得到错误信息,拒绝连接。

返回值:监听成功返回0,失败返回-1

if (listen(s_server, 10) < 0) {
	printf("套接字监听失败!\n");
	WSACleanup();
	return 0;
}

5.5 accept()函数

函数原型:SOCKET accept(SOCKET sockfd, struct sockaddr addr, socklen_t addrlen)
函数功能:接收客户端的连接

第一个参数:套接字

当使用了函数socket()成功建立,就会返回一个SOCKET类型的套接字,此时将返回的套接字给到accept()函数第一个参数位置

第二个参数:存储客户端的ip地址、端口号等信息
第三个参数:存储客户端信息的结构体的大小

返回值:一个新的套接字,它已经与客户端接收连接,后序的收、发数据都需要这个新的套接字

SOCKET s_accept; 返回连接成功的新的套接字
SOCKADDR_IN client_addr; 客户端的地址信息
int len = sizeof(SOCKADDR);
s_accept = accept(s_server, (SOCKADDR *)&client_addr, &len);
if (s_accept == SOCKET_ERROR) {
	printf("连接请求失败!\n");
	WSACleanup();
	return 0;
}

5.6 connect()函数

函数原型: int connect(SOCKET sockfd, const struct sockaddr FAR addr, int len)
功能:发送一个连接请求
第一个参数:套接字

使用socket()函数建立的套接字

第二个参数:存储连接服务端的ip地址和端口号等信息的结构体
第三个参数:结构体sockaddr的大小
返回值:成功返回0,否则返回SOCKET_ERROR

connect()函数示例:
if (connect(s_send, (SOCKADDR*)&client_addr, sizeof(SOCKADDR)) == SOCKET_ERROR){
	printf("服务端连接失败!\n");
	WSACleanup();
	return 0;
}

5.7 收recv()、发send()

函数原型 ssize_t recv(SOCKET sockfd, char FAR buf, int len, int flags)
函数功能:接收发送的数据
函数原型 ssize_t send(SOCKET sockfd, char FAR buf, int len, int flags)
函数功能:接收发送的数据
第一个参数:套接字

服务端
通过accent()函数与客户端建立了连接后返回的一个套接字


客户端
通过socket()函数创建的套接字

第二个参数:存放收到的数据的内存
第三个参数:存放数据内存的大小
第四个参数:填0

recv() 和send()将会阻塞,直到接收到至少一个字节的数据为止。这意味着 recv() 函数将一直等待,直到有数据可以接收,然后将接收到的数据存储在指定的缓冲区中,并返回接收到的字节数

当客户端与服务端成功连接成功后

服务端开始收、发数据
int send_len;
int recv_len;
char recv_buf[100]; 存储客户端发送过来的数据内存
char send_buf[100]; 存储从服务端回复给客户端的数据内存
while (1) {
	接收客户端发送过来的数据
	recv_len = recv(s_accept, recv_buf, 100, 0);
	if (recv_len < 0) {
		printf("接收失败!\n");
		break;
	}
	else {
		printf("客户端信息:%s\n", recv_buf);
	}
	printf("输入回复信息:");
	scanf("%s", send_buf);
	send_len = send(s_accept, send_buf, 100, 0);
	if (send_len < 0) {
		printf("发送失败!\n"); 
		break;
	}
}

客户端
//收发、数据
int send_len;
int recv_len;
char send_buf[100];
char recv_buf[100];

while (1) {
	printf("输入发送信息:");
	scanf("%s", send_buf);
	send_len = send(s_send, send_buf, 100, 0);
	if (send_len < 0) {
		printf("发送失败!\n");
		break;
	}
	recv_len = recv(s_send, recv_buf, 100, 0);
	if (recv_len < 0) {
		printf("接收失败!\n");
		break;
	}
	else {
		printf("服务端信息:%s\n", recv_buf);
	}
}
客户端这边先发送数据,所以先写函数send(),然后服务端先接收数据先写函数recv(),
当服务端接收到了数据存储到数组内存中,接着再写函数send()去发,最后客户端使用函数recv()去收,
这样循环,当客户端第一次的时候没有向服务端发送数据的时候,此时的服务端会停止等待客户端发送至少
超过1字节的数据,进行等待状态,当服务端没有向客户端回复数据,客户端那边的send()也会进入等待状态,
等待服务端发数据。

5.8 释放资源函数closesocket()、WSACleanup();

函数原型 int closesocket(SOCKET s)
功能:关闭套接字,释放套接字资源

函数 WSACleanup()
功能:关闭套接字库

5.9 函数htonl()、htons()、inet_addr()

函数原型 u_long htonl(u_long hostlong)
函数功能:将无符号长整型从主机字节顺序转换成网络字节序列

函数原型 u_short htons(u_short hostshort)
函数功能:将一个16位的无符号短整型从主机字节顺序转换成网络字节序列

函数原型 unsigned long inet_addr(const char FAR* cp)
函数功能:将由字符串表示的地址转换为32位的无符号长整型

当使用bind()函数的时候会用到将地址和端口号进行转换成网络字节序列
SOCKADDR_IN sin; 服务端地址结构体
int bind_return;
sin.sin_family = AF_INET; 地址族,必须是AF_INET 表示IPV4协议
sin.sin_port = htons(8888);设置端口号
sin.sin_addr.S_un.S_addr =  htonl(INADDR_ANY); 设置服务端地址
bind_return = bind(s_server, (SOCKADDR*) &sin, sizeof(SOCKADDR));

当初始化客户端连接指定的ip地址的时候就需要用到函数inet_addr()
将字符串“192.168.5.4”转换成整数192.168.5.4
使用在connect()函数
SOCKADDR_IN client_addr;
client_addr.sin_family = AF_INET;
client_addr.sin_addr.S_un.S_addr = inet_addr("192.168.5.4"); 

//inet_addr将字符串"192.168.31.114"转换成整型192.168.31.114

client_addr.sin_port = htons(8888);

if (connect(s_send, (SOCKADDR*)&client_addr, sizeof(SOCKADDR)) == SOCKET_ERROR){
	printf("服务端连接失败!\n");
	WSACleanup();
	return 0;
}

6 服务端代码

#include <stdio.h>
#include <winsock.h>

int main() {
	//1.初始化套接字库
	WORD w_req;
	WSADATA wsa_data;
	int err;
	w_req = MAKEWORD(2, 2);
	err = WSAStartup(w_req, &wsa_data);
	if (err != 0) {
		printf("初始化套接字库失败!\n");
		return 0;
	}
	//检测版本号
	if ((LOBYTE(wsa_data.wVersion) != 2) || HIBYTE(wsa_data.wVersion) != 2) {
		printf("套接字版本号不符!\n");
		WSACleanup();
	}

	//2.创建套接字
	SOCKET s_server;
	s_server = socket(AF_INET, SOCK_STREAM, 0);

	//3.绑定本地的地址
	SOCKADDR_IN sin; //服务端地址结构体
	int bind_return;
	sin.sin_family = AF_INET; //地址族,必须是AF_INET 表示IPV4协议
	sin.sin_port = htons(8888);//设置端口号
	sin.sin_addr.S_un.S_addr =  htonl(INADDR_ANY); //设置服务端地址
	bind_return = bind(s_server, (SOCKADDR*) &sin, sizeof(SOCKADDR));
	if (bind_return < 0) {
		printf("套接字绑定失败!\n");
		WSACleanup();
		return 0;
	}

	//4.设置套接字监听状态
	if (listen(s_server, 10) < 0) {
		printf("套接字监听失败!\n");
		WSACleanup();
		return 0;
	}
	printf("服务端正在监听,请稍后...\n");

	//5.接收连接请求
	SOCKET s_accept; //返回连接成功的新的套接字
	SOCKADDR_IN client_addr; //客户端的地址信息
	int len = sizeof(SOCKADDR);
	s_accept = accept(s_server, (SOCKADDR *)&client_addr, &len);
	if (s_accept == SOCKET_ERROR) {
		printf("连接请求失败!\n");
		WSACleanup();
		return 0;
	}
	printf("连接建立...准备接收数据\n");
	//开始收、发数据
	int send_len;
	int recv_len;
	char recv_buf[100]; //存储客户端发送过来的数据内存
	char send_buf[100]; //存储从服务端回复给客户端的数据内存
	while (1) {
		//接收客户端发送过来的数据
		recv_len = recv(s_accept, recv_buf, 100, 0);
		if (recv_len < 0) {
			printf("接收失败!\n");
			break;
		}
		else {
			printf("客户端信息:%s\n", recv_buf);
		}
		printf("输入回复信息:");
		scanf("%s", send_buf);
		send_len = send(s_accept, send_buf, 100, 0);
		if (send_len < 0) {
			printf("发送失败!\n"); 
			break;
		}
	}

	//关闭套接字
	closesocket(s_server);
	closesocket(s_accept);

	//释放动态资源库
	WSACleanup();
	return 0;
}

7.客户端代码

#include <stdio.h>
#include <winsock.h>

int main() {

	//1.初始化套接字库
	WORD w_req;
	WSADATA wsa_data;
	int err;
	w_req = MAKEWORD(2, 2);
	err = WSAStartup(w_req, &wsa_data);
	if (err != 0) {
		printf("初始化套接字库失败!\n");
		return 0;
	}
	//检测版本号
	if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) {
		printf("套接字库版本号不符!\n");
		WSACleanup();
		return 0;
	}

	//2.创建套接字
	SOCKET s_send;
	s_send = socket(AF_INET, SOCK_STREAM, 0);

	//3.初始化客户端的地址和端口号
	SOCKADDR_IN client_addr;
	client_addr.sin_family = AF_INET;
	client_addr.sin_addr.S_un.S_addr = inet_addr("192.168.5.4"); //inet_addr将字符串"192.168.31.114"转换成整型192.168.31.114
	client_addr.sin_port = htons(8888);

	//4.连接请求
	if (connect(s_send, (SOCKADDR*)&client_addr, sizeof(SOCKADDR)) == SOCKET_ERROR){
		printf("服务端连接失败!\n");
		WSACleanup();
		return 0;
	}
	printf("与服务端连接建立成功!\n");
	//收发、数据
	int send_len;
	int recv_len;
	char send_buf[100];
	char recv_buf[100];

	while (1) {
		printf("输入发送信息:");
		scanf("%s", send_buf);
		send_len = send(s_send, send_buf, 100, 0);
		if (send_len < 0) {
			printf("发送失败!\n");
			break;
		}
		recv_len = recv(s_send, recv_buf, 100, 0);
		if (recv_len < 0) {
			printf("接收失败!\n");
			break;
		}
		else {
			printf("服务端信息:%s\n", recv_buf);
		}
	}

	//关闭套接字
	closesocket(s_send);
	//释放套接字库资源
	WSACleanup();
	return 0;
}

8.运行代码

请添加图片描述

9.未知错误问题解决

1.运行代码后出现很多报错(没有添加ws3_32lib依赖库)
解决问题:

服务端编译器VS2019添加ws2_32.lib依赖库方法
1.打开 Visual Studio 2019,并在解决方案资源管理器中选择您的项目。
2.右键单击项目,并选择属性。或者,您也可以通过菜单栏的 “项目” -> “属性” 进入属性设置。
3.在项目属性窗口中,选择 “配置属性” -> “链接器” -> “输入”。
4.在 “附加依赖项” 字段中,点击下拉箭头。
5.在弹出的对话框中,点击 “添加” 按钮。
6.输入您需要添加的服务端链接库的名称,例如 “ws2_32.lib”。
7.点击 “确定” 按钮。
8.确认更改并重新编译您的项目。

2.客户端与服务端无法建立连接问题
在这里插入图片描述
需要更改客户端连接的ip地址

先运行服务端程序,服务端此时的窗口进入监听状态,监听客户端对指定的ip地址发起连接请求,当服务端监听到了客户端的连接请求就建立连接,否则没有建立连接就是服务端未监听到客户端的连接请求,此时的服务端一直在监听的状态。

在客户端设置连接的ip地址,不知道ip地址是多少的时候,服务端才能监听的到,服务端是可以监听到任意可用的网络ip地址,相当于自己家的wf网络ip地址。
在这里插入图片描述
在客户端代码中的ip地址设置,自家wf的IPV4的地址

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 以下是 TCP 套接字编程中使用 C 语言的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #define SERVER_PORT 8080 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in servaddr; // 创建套接字 sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror("socket"); exit(EXIT_FAILURE); } // 配置服务器地址信息 memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERVER_PORT); if (inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr) <= 0) { perror("inet_pton"); exit(EXIT_FAILURE); } // 连接服务器 if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) { perror("connect"); exit(EXIT_FAILURE); } // 发送数据 char *message = "Hello, world!"; if (send(sockfd, message, strlen(message), 0) == -1) { perror("send"); exit(EXIT_FAILURE); } // 接收数据 char buffer[1024]; int n; if ((n = recv(sockfd, buffer, sizeof(buffer) - 1, 0)) == -1) { perror("recv"); exit(EXIT_FAILURE); } buffer[n] = '\0'; printf("Received message: %s\n", buffer); // 关闭套接字 close(sockfd); return 0; } ``` 该程序通过创建套接字、连接服务器、发送数据、接收数据和关闭套接字等步骤实现了 TCP 套接字编程中的基本操作。其中,socket() 函数用于创建套接字,connect() 函数用于连接服务器,send() 函数用于发送数据,recv() 函数用于接收数据,close() 函数用于关闭套接字。 ### 回答2: TCP套接字是一种用于实现网络通信的协议。以下是一个简单的TCP套接字C语言代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #define MAX_BUFFER_SIZE 1024 int main() { int serverSocket, clientSocket; struct sockaddr_in serverAddr, clientAddr; char buffer[MAX_BUFFER_SIZE]; // 创建套接字 if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("套接字创建失败"); exit(EXIT_FAILURE); } // 设置服务器地址 serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器IP地址 serverAddr.sin_port = htons(8888); // 服务器端口号 // 绑定套接字到服务器地址 if (bind(serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1) { perror("绑定失败"); exit(EXIT_FAILURE); } // 监听连接 if (listen(serverSocket, 5) == -1) { perror("监听失败"); exit(EXIT_FAILURE); } printf("服务器等待客户端连接...\n"); // 接受客户端连接 socklen_t clientAddrLen = sizeof(clientAddr); if ((clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddr, &clientAddrLen)) == -1) { perror("接受连接失败"); exit(EXIT_FAILURE); } printf("客户端连接成功\n"); // 接收和发送数据 while (1) { memset(buffer, 0, sizeof(buffer)); // 接收数据 if (recv(clientSocket, buffer, sizeof(buffer), 0) == -1) { perror("接收数据失败"); exit(EXIT_FAILURE); } printf("接收到客户端消息:%s\n", buffer); // 发送数据 if (send(clientSocket, buffer, strlen(buffer), 0) == -1) { perror("发送数据失败"); exit(EXIT_FAILURE); } if (strcmp(buffer, "quit\n") == 0) { break; } } // 关闭套接字 close(clientSocket); close(serverSocket); return 0; } ``` 这段代码演示了一个简单的TCP服务器,它创建了一个套接字,绑定到本地IP地址和端口8888,并监听连接。一旦有客户端连接成功,服务器将接收来自客户端的消息,并发送相同的消息回复客户端。如果接收到的消息是"quit\n",服务器将关闭连接。 ### 回答3: TCP套接字是一种用于在计算机网络中进行可靠通信的协议。下面是一个利用C语言编写的TCP套接字的示例代码,用于在客户端和服务器之间进行通信。 (1)服务器端代码: ```C #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> int main() { int sockfd, newsockfd, portno, clilen; char buffer[256]; struct sockaddr_in serv_addr, cli_addr; int n; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("Error opening socket"); exit(1); } bzero((char *) &serv_addr, sizeof(serv_addr)); portno = 1234; serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(portno); if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { perror("Error on binding"); exit(1); } listen(sockfd, 5); clilen = sizeof(cli_addr); newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); if (newsockfd < 0) { perror("Error on accept"); exit(1); } bzero(buffer,256); n = read(newsockfd, buffer, 255); if (n < 0) { perror("Error reading from socket"); exit(1); } printf("Here is the message: %s\n",buffer); n = write(newsockfd,"I got your message",18); if (n < 0) { perror("Error writing to socket"); exit(1); } close(newsockfd); close(sockfd); return 0; } ``` (2)客户端代码: ```C #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> int main() { int sockfd, portno, n; struct sockaddr_in serv_addr; struct hostent *server; char buffer[256]; portno = 1234; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("ERROR opening socket"); exit(1); } server = gethostbyname("localhost"); if (server == NULL) { perror("Error, no such host"); exit(1); } bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); serv_addr.sin_port = htons(portno); if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { perror("Error connecting"); exit(1); } printf("Please enter the message: "); bzero(buffer, 256); fgets(buffer, 255, stdin); n = write(sockfd, buffer, strlen(buffer)); if (n < 0) { perror("Error writing to socket"); exit(1); } bzero(buffer, 256); n = read(sockfd, buffer, 255); if (n < 0) { perror("Error reading from socket"); exit(1); } printf("%s\n",buffer); close(sockfd); return 0; } ``` 以上就是一个简单的TCP套接字C语言代码示例,用于在服务器端和客户端之间进行通信。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值