打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Windows下C语言的Socket编程例子(TCP和UDP)
sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW); 基于TCP的socket编程是采用的流式套接字。在这个程序中,将两个工程添加到一个工作区。要链接一个ws2_32.lib的库文件。

服务器端编程的步骤:

1:加载套接字库,创建套接字(WSAStartup()/socket());

2:绑定套接字到一个IP地址和一个端口上(bind());

3:将套接字设置为监听模式等待连接请求(listen());

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

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

6:返回,等待另一连接请求;

7:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。

客户端编程的步骤:

1:加载套接字库,创建套接字(WSAStartup()/socket());

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

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

4:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。

函数介绍:

1.#include<sys/socket.h>
int socket (int family, int type, int protocol);

返回值,成功为非负整数,成为套接口描数字;出错为-1、

family参数:
AF_INET --ipv4;
AF_INET6--ipv6;
AF_LOCAL--unix域协议;
AF_ROUTE--路由socket;
AF_KEY--密钥socket;

type参数:
SOCK_STREAM--字节流套接口
SOCK_DGRAM--数据报套接口
SOCK_RAM--原始套接口

protocal参数一般置为0,当type为SOCK_RAM时不同

2.#include<sys/socket.h>
int connect (int sockfd, const struct sockaddr* servaddr, socklen_t addlen);

返回值,0-成功;-1-出错;

servaddr参数可以为sockaddr_in和sockaddr_in6类型,但是都必须强制转化为sockaddr指针类型;

connect函数的出错处理:
(1)ETIMEOUT-connection timed out 目的主机不存在,没有返回任何相应,例如主机关闭
(2)ECONNREFUSED-connection refused(硬错)到达目的主机后,由于各种原因建立不了连接,主机返回RST(复位)响应,例如主机监听进程未启用,tcp取消连接等
(3)EHOSTTUNREACH-no route to host(软错)路由上引发了一个目的地不可达的ICMP错误

其中(1)(3),客户端会进行定时多次重试,一定次数后才返回错误。另外,当connect连接失败时,sockfd套接口不可用,必须关闭后重新socket分配才行。

3.#include<sys/socket.h>
int bind (int sockfd, const struct sockaddr *addr, socklen_ t addrlen);


bind函数为套接口分配一个本地地址和端口
对于TCP协议,本地IP和端口可以不指定,主机会自动分配!
一般来说,我们会给服务器指定一个监听地址结构,以让客户端可以建立连接;连接建立后再分配一个临时端口作为数据传输通道。
而对于客户端来说,一般不bind本地地址,而是让内核根据输出协议自动选定本地ip和端口。

当ip地址和端口设置为通配符时,表示由内核自动分配:
(1)对于端口,设置为0表示通配符
(2)ip地址,对于ipv4,通配符表示如下:
struct sockaddr_in scr;
scr.sin_addr.s_addr=htonl (INADDR_ANY);
对于ipv6,通配符表示如下:
struct sockaddr_in6 scr;
scr.sin6_addr=in6addr_any;


4.#include<sys/socket.h>
int listen (int sockfd, int backlog);


backlog为最大连接个数,包括正在建立的连接和已经建立但是还未进行accept函数的连接,即处于三次握手机制和accept函数之间。

listen函数将sockfd套接口由主动模式变成被动监听模式,使内核能接受指向此套接口的连接请求 。
当队列已满时,服务器不会对新来的SYN有任何回应(包括RST),从而使得客户端延时重发SYN。

5.#include<sys/socket.h>
int accept (int sockfd, struct sockaddr * addr, socklen_t *len);

成功,返回非负描数字,称为已连接套接口;-1表示出错

sockfd为listen函数后的监听套接口;
addr和len参数都为 值-结果 参数;
addr返回客户进程的协议地址,功能类似getpeername函数
len参数调用前是addr的长度,返回后是内核读取的addr结构的准确字节数。
如果对客户进程不感兴趣,addr`len可设为NULL;

accept函数返回一个由内核自动生成的新描述字,称为 已连接套接口;
一般来说,sockfd称为监听套接口,在服务器进程一直存在;accept返回的已连接套接口,在服务器子进程中存在,一旦子进程连接结束,此套接口也关闭。

6.通常的处理,服务器当进程accept后,会fork一个子进程,同时,父进程关闭已连接套接口,子进程关闭监听套接口,这样2个进程就能各司其职:父进程继续监听,子进程完成连接操作。

connfd=accept(sockfd,...,...);
if (fork()==0){    //子进程
close(sockfd); 
处理过程。。。
close(connfd);
}
close(connfd);//父进程

7。我们可以通过getsockname函数来获得套接口的本地ip信息,通过getpeername函数来获得连接另外一端的ip信息。
#include<sys/socket.h>
int getsockname (int sockfd, struct sockaddr *addr, socklen_t *len);
得到套接口关联的本地地址结构
int getpeername (int sockfd, struct sockaddr *addr, socklen_t *len);

得到远程协议地址结构   len为struct sockaddr的长度

当中的sockfd必须是已连接套接口,不能是监听套接口。

8.int close (int sockfd);
close只是减少某个套接口的计数,只有当计数为0时,内核才会向对方主机发送FIN。。。

如果确实需要发送FIN,那可以使用shutdown函数
int shutdown(int sockfd, int flags);
flags为SHUT_WR,  SHUT_RD,  SHUT_RDWR..关闭套接口的相应功能,(读,写),并发送FIN,不管计数是否为0。
1、TCP
///////////////////////////////////////////////////Server///////////////////////////////////////////////////////////
#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
int main(int argc, char* argv[])
{
    //初始化WSA
    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 !");
        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();
    return 0;
}
///////////////////////////////////////////////////client///////////////////////////////////////////////////////////
#include "stdafx.h"
#include <WINSOCK2.H>
#include <STDIO.H>
#pragma  comment(lib,"ws2_32.lib")

int main(int argc, char* argv[])
{
    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;
    }
    sockaddr_in serAddr;
    serAddr.sin_family = AF_INET;
    serAddr.sin_port = htons(8888);
    serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
    {
        printf("connect error !");
        closesocket(sclient);
        return 0;
    }
    char * sendData = "你好,TCP服务端,我是客户端!\n";
    send(sclient, sendData, strlen(sendData), 0);
    char recData[255];
    int ret = recv(sclient, recData, 255, 0);
    if(ret > 0)
    {
        recData[ret] = 0x00;
        printf(recData);
    }
    closesocket(sclient);
    WSACleanup();
    return 0;
}
 
 
2、UDP
/////////////////////////////Server////////////////////////////////////////////////////////
#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
int main(int argc, char* argv[])
{
    WSADATA wsaData;
    WORD sockVersion = MAKEWORD(2,2);
    if(WSAStartup(sockVersion, &wsaData) != 0)
    {
        return 0;
    }
    SOCKET serSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(serSocket == INVALID_SOCKET)
    {
        printf("socket error !");
        return 0;
    }
    sockaddr_in serAddr;
    serAddr.sin_family = AF_INET;
    serAddr.sin_port = htons(8888);
    serAddr.sin_addr.S_un.S_addr = INADDR_ANY;
    if(bind(serSocket, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
    {
        printf("bind error !");
        closesocket(serSocket);
        return 0;
    }
   
    sockaddr_in remoteAddr;
    int nAddrLen = sizeof(remoteAddr);
    while (true)
    {
        char recvData[255]; 
        int ret = recvfrom(serSocket, recvData, 255, 0, (sockaddr *)&remoteAddr, &nAddrLen);
        if (ret > 0)
        {
            recvData[ret] = 0x00;
            printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
            printf(recvData);           
        }
        char * sendData = "一个来自服务端的UDP数据包\n";
        sendto(serSocket, sendData, strlen(sendData), 0, (sockaddr *)&remoteAddr, nAddrLen);   
    }
    closesocket(serSocket);
    WSACleanup();
    return 0;
}
/////////////////////////////client////////////////////////////////////////////////////////
#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
int main(int argc, char* argv[])
{
    WORD socketVersion = MAKEWORD(2,2);
    WSADATA wsaData;
    if(WSAStartup(socketVersion, &wsaData) != 0)
    {
        return 0;
    }
    SOCKET sclient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
   
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(8888);
    sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    int len = sizeof(sin);
   
    char * sendData = "来自客户端的数据包.\n";
    sendto(sclient, sendData, strlen(sendData), 0, (sockaddr *)&sin, len);
    char recvData[255];    
    int ret = recvfrom(sclient, recvData, 255, 0, (sockaddr *)&sin, &len);
    if(ret > 0)
    {
        recvData[ret] = 0x00;
        printf(recvData);
    }
    closesocket(sclient);
    WSACleanup();
    return 0;
}
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
tcp 客户端和服务端代码示例
网络socket编程指南
Linux下getsockopt/setsockopt 函数说明 - xioahw的专栏 ...
转udp 超时设置(select函数的一种用法)
HTTP服务器初步,仅仅实现两端通信
socket网络编程快速上手(二)——细节问题(5)(完结篇)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服