SOCKET编程(C语⾔实现)
⼀,基本步骤
TCP server端实现
1,建⽴socket套接字
2,绑定套接字(指定ip和端⼝号)
3,listen(进⼊监听状态)
4,accept接受客户端请求
5,收发数据
5,关闭套接字
TCP client端实现
1,建⽴socket套接字
2,connect连接服务器套接字(指定服务器ip和端⼝号)
3,收发数据
4,关闭套接字
UDP的步骤相似,server端没有listen和accept步骤,client不需要connect步骤。如果client端不显⽰的指定ip和端⼝号,server端需要bind⾃⼰的ip和端⼝号,传输层才能将通过该端⼝的数据送给服务器。
Windows下函数原型:
socket:
SOCKET socket(int af, int type, int protocol)
af 为地址族(Address Family),也就是 IP 地址类型,常⽤的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET
是“Inetnet”的简写。AF_INET 表⽰ IPv4 地址,例如 127.0.0.1;AF_INET6 表⽰ IPv6 地址,例如
1030::C9B4:FF12:48AA:1A2B。
返回值是socket的ID值,标识socket的唯⼀性,是⼀个整形数。
介绍⼏个重要的结构体
struct sockaddr_in{
sa_family_t sin_family; //地址族(Address Family),也就是地址类型
uint16_t sin_port; //16位的端⼝号
struct in_addr sin_addr; //32位IP地址
char sin_zero[8]; //不使⽤,⼀般⽤0填充
};
struct in_addr{
in_addr_t s_addr; //32位的IP地址
};
struct sockaddr{
sa_family_t sin_family; //地址族(Address Family),也就是地址类型
char sa_data[14]; //IP地址和端⼝号
};
结构体sockaddr和sockaddr_in等价,sockaddr_in包含详细的ip地址和端⼝号,所以sockaddr_in更加利于阅读和使⽤,编程的时候多使⽤它。
bind:
int bind(SOCKET sock, const struct sockaddr *addr, int addrlen)
将套接字和ip地址和端⼝绑定。
connect:
int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen)
各参数和bind函数⼀样,区别在于,connect函数是client端⽤于和server端建⽴连接。
listen:
int listen(SOCKET sock, int backlog);
sock 为需要进⼊监听状态的套接字,backlog 为请求队列的最⼤长度。
所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。
accept:
SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);
当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求。
它的参数与 listen() 和 connect() 是相同的:sock 为服务器端套接字,addr 为 sockaddr_in 结构体变量,addrlen 为参数 addr 的长度,可由 sizeof() 求得。
accept() 返回⼀个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端⼝号,⽽ sock 是服务器端的套接字,⼤家注意区分。后⾯和客户端通信时,要使⽤这个新⽣成的套接字,⽽不是原来服务器端的套接字。
最后需要说明的是:listen() 只是让套接字进⼊监听状态,并没有真正接收客户端请求,listen() 后⾯的代码会继续执⾏,直到遇到accept()。accept() 会阻塞程序执⾏(后⾯代码不能被执⾏),直到有新的请求到来。
Windows下数据的接收和发送
send:
从服务器端发送数据使⽤ send() 函数,它的原型为:
int send(SOCKET sock, const char *buf, int len, int flags);
sock 为要发送数据的套接字,buf 为要发送的数据的缓冲区地址,len 为要发送的数据的字节数,flags 为发送数据时的选项。
返回值和前三个参数不再赘述。
第四个参数flahs⼀般置为0,或是以下的组合:
MSG_DONTROUTE:不查路由表
MSG_OOB:接受或发送带外数据
MSG_PEEK:查看数据,并不从系统缓冲区移⾛数据
MSG_WAITALL :等待任何数据
MSG_DONTROUTE:是send函数使⽤的标志.这个标志告诉IP协议.⽬的主机在本地⽹络上⾯,没有必要查路由表.这个标志⼀般⽤⽹络诊断和路由程式⾥⾯。
MSG_OOB:表⽰能够接收和发送带外的数据.
MSG_PEEK:是recv函数的使⽤标志,表⽰只是从系统缓冲区中读取内容,⽽不清除系统缓冲区的内容。这样下次读的时候,仍然是相同的内容。⼀般在有多个进程读写数据时能够使⽤这个标志。
MSG_WAITALL:是recv函数的使⽤标志,表⽰等到任何的信息到达时才返回。使⽤这个标志的时候recv会⼀直阻塞,直到指定的条件满⾜,或是发⽣了错误。
1)当读到了指定的字节时,函数正常返回,返回值等于len
2)当读到了⽂档的结尾时,函数正常返回.返回值⼩于len
3)当操作发⽣错误时,返回-1,且配置错误为相应的错误号(errno)
recv:
在客户端接收数据使⽤ recv() 函数,它的原型为:
int recv(SOCKET sock, char *buf, int len, int flags);
函数参数和send相同。
下⾯是经典的回声客户端的实现(TCP连接):
server端代码:
#pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll
#define BUF_SIZE 100
int main(){
WSADATA wsaData;
WSAStartup( MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET servSock = socket(AF_INET, SOCK_STREAM, 0);
//绑定套接字
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都⽤0填充
sockAddr.sin_family = PF_INET; //使⽤IPv4地址
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
sockAddr.sin_port = htons(1234); //端⼝
bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//进⼊监听状态
listen(servSock, 20);
//接收客户端请求
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);
char buffer[BUF_SIZE]; //缓冲区
int strLen = recv(clntSock, buffer, BUF_SIZE, 0); //接收客户端发来的数据
send(clntSock, buffer, strLen, 0); //将数据原样返回
//关闭套接字
closesocket(clntSock);
closesocket(servSock);
/
/终⽌ DLL 的使⽤
WSACleanup();
return0;
}
client端代码:
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll
#define BUF_SIZE 100
int main(){
//初始化DLL
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//向服务器发起请求
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都⽤0填充
sockAddr.sin_family = PF_INET;
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
sockAddr.sin_port = htons(1234);
connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//获取⽤户输⼊的字符串并发送给服务器socket编程聊天室基本流程
char bufSend[BUF_SIZE] = {0};
printf("Input a string: ");
scanf("%s", bufSend);
send(sock, bufSend, strlen(bufSend), 0);
//接收服务器传回的数据
char bufRecv[BUF_SIZE] = {0};
recv(sock, bufRecv, BUF_SIZE, 0);
//输出接收到的数据
printf("Message form server: %s\n", bufRecv);
//关闭套接字
closesocket(sock);
/
/终⽌使⽤ DLL
WSACleanup();
system("pause");
return0;
}
注意:在TCP连接中,client端的socket的ip和端⼝没有指定,在建⽴连接的时候,TCP协议会分配⼀个端⼝号
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论