Windows下C语言socket编程

Windows下C语言socket编程在 Windows 下利用 C 语言实现 socket 通信 刚开始学习 记录自己的学习过程 如果出现错误还望各位大佬指点一二 一 原理 1 socket 函数 intsocket intdomain inttype intprotocol domain 协议域 决定了 socket 的地址类型 在通信中必须采用对应的地址 type 指定 socket 类型 常用的 socket 类型

在Windows下利用C语言实现socket通信,刚开始学习,记录自己的学习过程,如果出现错误还望各位大佬指点一二。

一、原理

Windows下C语言socket编程

1.socket()函数

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

domain:协议域,决定了socket的地址类型,在通信中必须采用对应的地址。

type:指定socket类型。常用的socket类型有,SOCK_STREAMSOCK_DGRAMSOCK_RAWSOCK_PACKETSOCK_SEQPACKET等。

protocol:指定协议。常用的协议有,IPPROTO_TCPIPPTOTO_UDPIPPROTO_SCTPIPPROTO_TIPC等。

 

名称

目的

AF_UNIX, AF_LOCAL

本地通信

AF_INET

IPv4网络通信

AF_INET6

IPv6网络通信

AF_PACKET

链路层通信

 

Linux系统中AF_*PF_*是等价的。

更多的socket函数的domain、type、protocol解析详见https://blog.csdn.net/liuxingen/article/details/

2.bind()函数

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

sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socketbind()函数就是将给这个描述字绑定一个名字。

addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。

addrlen:对应的地址长度。

3.listen()connect()函数

int listen(int sockfd, int backlog);

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

如果作为一个服务器,在调用socket()bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。

listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。

4.accept()函数

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

accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。

5.read()write()函数

recv()/send()

readv()/writev()

recvmsg()/sendmsg()

recvfrom()/sendto()

以recv()为例

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

s:一个标识已连接套接口的描述字。

buf:用于接收数据的缓冲区。

len:缓冲区长度。

flags:指定调用方式

6.close()函数

int close(int fd);

 

7. sockaddr_insockaddr

处理网络通信的地址

sockaddr在头文件#include

socket.h
>
中定义,
sockaddr
的缺陷是:
sa_data
把目标地址和端口信息混在一起

sockaddr_in在头文件#include<netinet/in.h>#include <arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把portaddr 分开储存在两个变量中

二者长度一样,都是16个字节,即占用的内存大小是一致的,因此可以互相转化。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr

sockaddr常用于bindconnectrecvfromsendto等函数的参数,指明地址信息,是一种通用的套接字地址。

sockaddr_in internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数:sockaddr_in用于socket定义和赋值;sockaddr用于函数参数。

注:这两个函数的头文件

Linux

#include <netinet/in.h>

#include

socket.h
>

Windows

 #include

#pragma comment(lib,”Ws2_32.lib “)

https://blog.csdn.net/will130/article/details/

关于这些函数更详细的总结参考https://blog.csdn.net/hellokitty136/article/details/

 

socket各种错误码

1.INVALID_SOCKET : 表示该 socket fd 无效。如 accept(2) socket(2) 等在创建socketfd

2.SOCKET_ERROR : 如调用bind(2)listen(2)connect(2)send(2)setsockopt(2)fcntl(2)等函数时出错则会返回该

3.socket在非阻塞的情况下接收和发送数据时的错误码以及如何处理

1)判断socket是否“阻塞”(即检测错误类型 errno 是否为 EINPROGRESS 或 EWOULDBLOCK,该情况往往是由于TCP窗口导致的

2)若错误码 errno 为 EINTR(被信号中断了,则继续重试),则忽略该错误,继续接收或发送数据。

3)若recvsend调用后返回 0 ,则表示对端关闭了连接。

更多的详见https://blog.csdn.net/u0/article/details/

二、实现

2.1 server

#include 
  
    #include 
   
     #include 
    
      #pragma comment(lib, "ws2_32.lib") #define PORT 1500; //端口号 #define BACKLOG 5; //最大监听数 int main(int argc, char* argv[]) { //初始化WSA windows下异步套接字的启动命令 WORD sockVersion = MAKEWORD(2, 2); WSADATA wsaData; if (WSAStartup(sockVersion, &wsaData) != 0) { return 0; } //创建套接字 SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (slisten == INVALID_SOCKET) { printf("socket error !"); return 0; } //绑定IP和端口 sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(8888); sin.sin_addr.S_un.S_addr = INADDR_ANY; if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR) { printf("bind error !"); } //开始监听 if (listen(slisten, 5) == SOCKET_ERROR) { printf("Listen error!\n"); return 0; } //循环接收数据 SOCKET sClient; sockaddr_in remoteAddr; int nAddrlen = sizeof(remoteAddr); char revData[255]; while (true) { printf("等待连接……\n"); sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen); if (sClient == INVALID_SOCKET) { printf("accept error !"); continue; } printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr)); //接收数据 int ret = recv(sClient, revData, 255, 0); if (ret > 0) { revData[ret] = 0x00; printf(revData); } //发送数据 char * sendData = "TCP客户端!\n"; send(sClient, sendData, strlen(sendData), 0); closesocket(sClient); } closesocket(slisten); WSACleanup(); system("pause"); return 0; } 
     
    
  

2.2 client

#include 
  
    #include 
   
     #pragma comment(lib,"ws2_32.lib") int main(int argc, char* argv[]) { //初始化WSA WORD sockVersion = MAKEWORD(2, 2); WSADATA data; if (WSAStartup(sockVersion, &data) != 0) { return 0; } //创建套接字 SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sclient == INVALID_SOCKET) { printf("invalid socket !"); return 0; } //绑定IP和端口 sockaddr_in serAddr; serAddr.sin_family = AF_INET; serAddr.sin_port = htons(8888); serAddr.sin_addr.S_un.S_addr = inet_addr("192.168.0.102");//127.0.0.1 本地地址或者127.0.0.1都可以 if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR) { printf("connect error !"); closesocket(sclient); system("pause"); return 0; } //发送数据 char * sendData = "TCP服务端,我是客户端!\n"; send(sclient, sendData, strlen(sendData), 0); printf(sendData); char recData[255]; int ret = recv(sclient, recData, 255, 0); if (ret > 0) { recData[ret] = 0x00; printf(recData); } //关闭 closesocket(sclient); WSACleanup(); system("pause"); return 0; } 
    
  

代码实现参考https://www.cnblogs.com/churi/archive/2013/02/27/2935427.html

三、调试运行

使用vs2015

首先,执行server程序

然后,在右侧解决方案一栏中单击选择client解决方案,右键->调试->启动新实例,即可运行client程序。

3.1错误及解决

a.C4996    ‘inet_addr’: Use inet_pton() or InetPton() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings    SocketClient    g:\c\oppo\socketclient\client.cpp    27    

项目->属性->c/c++->SDL检查 改成 否(/sdl-)

b.一直显示“connect error”

server和client的端口设置不一致,serAddr.sin_port = htons(8888);更改为一个端口号即可;

inet_addr()设置错误,设置为本地IP或者127.0.0.1都可以。

有可能是防火墙的问题,关闭防火墙进行调试。(我没有关闭也能执行,看一些文章中说的需要关闭)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/211141.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午11:06
下一篇 2026年3月18日 下午11:07


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号