标签(空格分隔): Linux Socket Cpp/C# Socket Struct —
1.内容目录
[TOC]
2. 网络中的进程如何通信
本地进程之间可以通过PID来唯一标识一个程序,但是网络中不同计算机之间的程序不是通过PID,而是通过TCP/IP协议族来区分不同计算机,通过网络层的IP地址可以唯一标识一个网络主机,而传输层“协议+端口”可以唯一标识主机中的进程。所以利用“IP地址、协议、端口”就可以进行网络通信了。使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(已很少使用),就目前而言,大量的程序都通过SOCKET方式进行通信,兼容性较好。
2. Socket简介
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。 建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。 百度百科socket 简单了解socket
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。
socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的,撰写者为Stephen Carr、Steve Crocker和Vint Cerf。根据美国计算机历史博物馆的记载,Croker写道:“命名空间的元素都可称为套接字接口。一个套接字接口构成一个连接的一端,而一个连接可完全由一对套接字接口规定。”计算机历史博物馆补充道:“这比BSD的套接字接口定义早了大约12年。
3. Socket的基本操作
3.1 socket()函数
int socket(int domain, int type, int protocol); //for example int listened = socket(AF_INET, SOCK_STREAM, 0);
socket函数用于创建一个socket标识符来唯一标识一个socket,类似于文件描述字。 函数的三个参数分别为:
- domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
- type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等(socket的类型有哪些?)。
- protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议)。
注意:并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。
当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。
###3.2 bind()函数
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//for example
int listenfd, connfd;
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);
bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
函数的三个参数为:
- sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
- addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,如ipv4对应的是:
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
struct sockaddr_in6 {
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* port number */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */
};
struct in6_addr {
unsigned char s6_addr[16]; /* IPv6 address */
};
- addrlen:对应的是地址的长度。
通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。
网络字节序与主机字节序 主机字节序就是我们平常说的大端和小端模式:不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。引用标准的Big-Endian和Little-Endian的定义如下: a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。 网络字节序:4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。这种传输次序称作大端字节序。由于TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。字节序,顾名思义字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序,一个字节的数据没有顺序的问题了。 所以:在将一个地址绑定到socket的时候,请先将主机字节序转换成为网络字节序,而不要假定主机字节序跟网络字节序一样使用的是Big-Endian。由于这个问题曾引发过血案!公司项目代码中由于存在这个问题,导致了很多莫名其妙的问题,所以请谨记对主机字节序不要做任何假定,务必将其转化为网络字节序再赋给socket。
3.3 listen()、connect()函数
- 作为一个服务器在调用socket() & bind()后,需要调用listen()来监听socket,如果客户端调用connnect()发出连接请求,那么服务器就会接受到这个请求。
int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。 socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。
connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。
3.4 accept()函数
- TCP服务器 依次调用 socket();bind();listen()之后,就会监听指定的socket地址。
- TCP客户端 依次调用 socket();bind();connect()之后,就会像TCP服务器发送连接请求。TCP服务器监听到了这个请求之后就会调用accept()函数要接受请求,然后连接就建立好了,可以进行网络I/O操作。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
注意:accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。
3.5 I/O函数
万事具备只欠东风,至此服务器与客户已经建立好连接了。可以调用网络I/O进行读写操作了,即实现了网咯中不同进程之间的通信!网络I/O操作有下面几组:
- read()/write()
- recv()/send()
- readv()/writev()
- recvmsg()/sendmsg()
- recvfrom()/sendto()
我推荐使用recvmsg()/sendmsg()函数,这两个函数是最通用的I/O函数,实际上可以把上面的其它函数都替换成这两个函数。
3.6 close()函数
在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。
#include <unistd.h>
int close(int fd);
close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。
注意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。
Linux客户端发送数据
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<iostream>
#include<string>
#define MAXLINE 4096
using std::string;
int main(int argc, char** argv)
{
int sockfd, n, rec_len;
char recvline[MAXLINE], buf[MAXLINE];//, sendline[4096]
string sendmsg = "hello word hello socket\n";
struct sockaddr_in servaddr;
if( argc != 2){
printf("usage: ./client <ipaddress>\n");
exit(0);
}
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);
if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s\n",argv[1]);
exit(0);
}
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("send msg to server: ");
//fgets(sendline, 4096, stdin);
const char *sendline = sendmsg.c_str();
std::cout<<sendmsg<<std::endl;
if( send(sockfd, sendline, strlen(sendline), 0) < 0)
{
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
if((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) {
printf("recv msg error: %s(errno: %d)\n", strerror(errno), errno);
//perror("recv error");
exit(1);
}
buf[rec_len] = '\0';
printf("Received : %s ",buf);
close(sockfd);
exit(0);
}
Linux 服务端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 4096
int main(int argc, char** argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[4096];
int n;
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
if( listen(listenfd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("======waiting for client's request======\n");
while(1){
if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
n = recv(connfd, buff, MAXLINE, 0);
buff[n] = '\0';
printf("recv msg from client: %s\n", buff);
if(send(connfd, "Hello,you are connected!\n", 26,0) == -1)
{
printf("send msg error: %s(errno: %d)",strerror(errno),errno);
perror("send error");
continue;
}
close(connfd);
}
close(listenfd);
}
Windows下python服务端接收数据
#!/usr/bin/python
# -- coding: utf-8 --
import socket;
if "__main__" == __name__:
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM); #定义socket类型,网络通信,TCP
print("create socket succ!");
sock.bind(('192.168.177.111', 6666)); #套接字绑定的IP与端口
print("bind socket succ!");
sock.listen(5);
print("listen succ!");
except:
print("init socket err!");
while True:
print("listen for client...");
conn, addr = sock.accept(); #接受TCP连接,并返回新的套接字与IP地址
print("get client");
print(addr);
conn.settimeout(5);
szBuf = conn.recv(1024); #把接收的数据实例化
print("recv:" + str(szBuf));
if "0" == szBuf:
conn.sendall('exit');
else:
conn.sendall('welcome client!');
conn.close();
print("end of sevice");
未测试代码
以下代码未测试,供参考 python版本
root@yangrong:/python# catday5-socket-server.py #!/usr/bin/python import socket #socket模块 import commands #执行系统命令模块 HOST='10.0.0.245' PORT=50007 s= socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定义socket类型,网络通信,TCP s.bind((HOST,PORT)) #套接字绑定的IP与端口 s.listen(1) #开始TCP监听,监听1个请求 while 1: conn,addr=s.accept() #接受TCP连接,并返回新的套接字与IP地址 print'Connected by',addr #输出客户端的IP地址 while 1: data=conn.recv(1024) #把接收的数据实例化 cmd_status,cmd_result=commands.getstatusoutput(data) #commands.getstatusoutput执行系统命令(即shell命令),返回两个结果,第一个是状态,成功则为0,第二个是执行成功或失败的输出信息 if len(cmd_result.strip()) ==0: #如果输出结果长度为0,则告诉客户端完成。此用法针对于创建文件或目录,创建成功不会有输出信息 conn.sendall('Done.') else: conn.sendall(cmd_result) #否则就把结果发给对端(即客户端) conn.close() #关闭连接
root@yangrong:/python# catday5-socket-client.py #!/usr/bin/python import socket HOST='10.0.0.245' PORT=50007 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定义socket类型,网络通信,TCP s.connect((HOST,PORT)) #要连接的IP与端口 while 1: cmd=raw_input("Please input cmd:") #与人交互,输入命令 s.sendall(cmd) #把命令发送给对端 data=s.recv(1024) #把接收的数据定义为变量 print data #输出变量 s.close() #关闭连接
来源: http://blog.csdn.net/rebelqsp/article/details/22109925
Linux C ```c #include
#include #include #include #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h>
#define MAXLINE 4096
int main(int argc, char** argv) { int listenfd, connfd; struct sockaddr_in servaddr; char buff[4096]; int n;
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
if( listen(listenfd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("======waiting for client's request======\n");
while(1){
if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
n = recv(connfd, buff, MAXLINE, 0);
buff[n] = '\0';
printf("recv msg from client: %s\n", buff);
close(connfd);
}
close(listenfd); } ``` ```c #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h>
#define MAXLINE 4096
int main(int argc, char** argv) { int sockfd, n; char recvline[4096], sendline[4096]; struct sockaddr_in servaddr;
if( argc != 2){
printf("usage: ./client <ipaddress>\n");
exit(0);
}
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);
if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s\n",argv[1]);
exit(0);
}
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("send msg to server: \n");
fgets(sendline, 4096, stdin);
if( send(sockfd, sendline, strlen(sendline), 0) < 0)
{
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
close(sockfd);
exit(0); } ``` >貌似是一个多线程的,还没学过多线程编程,看看吧 ```c /* File Name: server.c */ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #define DEFAULT_PORT 8000 #define MAXLINE 4096 int main(int argc, char** argv) {
int socket_fd, connect_fd;
struct sockaddr_in servaddr;
char buff[4096];
int n;
//初始化Socket
if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//初始化
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT
//将本地地址绑定到所创建的套接字上
if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//开始监听是否有客户端连接
if( listen(socket_fd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("======waiting for client's request======\n");
while(1){ //阻塞直到有客户端连接,不然多浪费CPU资源。
if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
} //接受客户端传过来的数据
n = recv(connect_fd, buff, MAXLINE, 0); //向客户端发送回应数据
if(!fork()){ /*紫禁城*/
if(send(connect_fd, "Hello,you are connected!\n", 26,0) == -1)
perror("send error");
close(connect_fd);
exit(0);
}
buff[n] = '\0';
printf("recv msg from client: %s\n", buff);
close(connect_fd);
}
close(socket_fd); } ```
/* File Name: client.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 4096
int main(int argc, char** argv)
{
int sockfd, n,rec_len;
char recvline[4096], sendline[4096];
char buf[MAXLINE];
struct sockaddr_in servaddr;
if( argc != 2){
printf("usage: ./client <ipaddress>\n");
exit(0);
}
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8000);
if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s\n",argv[1]);
exit(0);
}
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("send msg to server: \n");
fgets(sendline, 4096, stdin);
if( send(sockfd, sendline, strlen(sendline), 0) < 0)
{
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
if((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) {
perror("recv error");
exit(1);
}
buf[rec_len] = '\0';
printf("Received : %s ",buf);
close(sockfd);
exit(0);
}
Unity3D Socket通讯
Unity3D 中进行Socket采用的是C#, 其基本流程与CPP与Python无异, 把相应的函数对应到不同语言上就行,思路是一样的。
-
MonoBehaviour类中的方法尽量别用了,会出现不可预知的bug。
-
自己起的线程,系统不会掺和,自己找个合适的地方再把他kill掉。
已测试如下代码,需要把接受数据的方式从二进制流改成ASCII码形式。
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System;
/// <summary>
/// scoket服务器监听端口脚本
/// </summary>
public class SocketServer : MonoBehaviour
{
private Thread thStartServer;//定义启动socket的线程
void Start()
{
thStartServer = new Thread(StartServer);
thStartServer.Start();//启动该线程
}
void Update()
{
}
private void StartServer()
{
const int bufferSize = 8792;//缓存大小,8192字节
IPAddress ip = IPAddress.Parse("192.168.0.13");
TcpListener tlistener = new TcpListener(ip, 10001);
tlistener.Start();
Debug.Log("Socket服务器监听启动......");
TcpClient remoteClient = tlistener.AcceptTcpClient();//接收已连接的客户端,阻塞方法
Debug.Log("客户端已连接!local:" + remoteClient.Client.LocalEndPoint + "<---Client:" + remoteClient.Client.RemoteEndPoint);
NetworkStream streamToClient = remoteClient.GetStream();//获得来自客户端的流
do
{
try //直接关掉客户端,服务器端会抛出异常
{
//接收客户端发送的数据部分
byte[] buffer = new byte[bufferSize];//定义一个缓存buffer数组
int byteRead = streamToClient.Read(buffer, 0, bufferSize);//将数据搞入缓存中(有朋友说read()是阻塞方法,测试中未发现程序阻塞)
if (byteRead == 0)//连接断开,或者在TCPClient上调用了Close()方法,或者在流上调用了Dispose()方法。
{
Debug.Log("客户端连接断开......");
break;
}
string msg = Encoding.Unicode.GetString(buffer, 0, byteRead);//从二进制转换为字符串对应的客户端会有从字符串转换为二进制的方法
Debug.Log("接收数据:" + msg + ".数据长度:[" + byteRead + "byte]");
}
catch(Exception ex)
{
Debug.Log("客户端异常:"+ex.Message);
break;
}
}
while (true);
}
void OnApplicationQuit()
{
thStartServer.Abort();//在程序结束时杀掉线程,想起以前老谢给我讲的,起线程就像拉屎,完事一定要记得自己擦,系统不会给你擦,经测试不擦第二次启动unity会无响应
}
}
客户端程序
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System;
using System.Text;
public class SocketClient : MonoBehaviour
{
void Start()
{
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Alpha1))
{
Client();
}
if (Input.GetKeyDown(KeyCode.Alpha2))
{
SimulateMultiUserClient();
}
}
TcpClient client;
private void Client()
{
client = new TcpClient();
try
{
client.Connect(IPAddress.Parse("192.168.0.13"), 10001);//同步方法,连接成功、抛出异常、服务器不存在等之前程序会被阻塞
}
catch (Exception ex)
{
Debug.Log("客户端连接异常:" + ex.Message);
}
Debug.Log("LocalEndPoint = " + client.Client.LocalEndPoint + ". RemoteEndPoint = " + client.Client.RemoteEndPoint);
//客户端发送数据部分
for (int i = 0; i < 2; i++)
{
try
{
string msg = "hello server i am No " + i;
NetworkStream streamToServer = client.GetStream();//获得客户端的流
byte[] buffer = Encoding.Unicode.GetBytes(msg);//将字符串转化为二进制
streamToServer.Write(buffer, 0, buffer.Length);//将转换好的二进制数据写入流中并发送
Debug.Log("发出消息:" + msg);
}
catch(Exception ex)
{
Debug.Log("服务端产生异常:"+ex.Message);
}
}
}
private void SimulateMultiUserClient()
{
for (int i = 0; i < 2; i++)
{
client = new TcpClient();
try
{
client.Connect(IPAddress.Parse("192.168.0.13"), 10001);//连接到服务器端
}
catch (Exception ex)//抛出连接错误的异常
{
Debug.Log("客户端连接异常:" + ex.Message);
}
Debug.Log("LocalEndPoint = " + client.Client.LocalEndPoint + ". RemoteEndPoint = " + client.Client.RemoteEndPoint);
//客户端发送数据部分
string msg = "hello server i am No " + i;
NetworkStream streamToServer = client.GetStream();//获得客户端的流
byte[] buffer = Encoding.Unicode.GetBytes(msg);//将字符串转化为二进制
streamToServer.Write(buffer, 0, buffer.Length);//将转换好的二进制数据写入流中并发送
Debug.Log("发出消息:" + msg);
}
}
private void SendMsgToServer(string content)
{
}
}
// 未测试
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
private string showMessage = "";
// Use this for initialization
private byte[] resultBytes = new byte[1024];
private int myPort = 8885;
private Socket serverSocket;
//
void Start ()
{
string hostName = Dns.GetHostName();
Debug.Log(hostName);
System.Net.IPAddress[] addressList = Dns.GetHostAddresses(hostName);
foreach (IPAddress ipAddress in addressList)
{
Debug.Log(ipAddress.ToString());
}
showMessage += "\n" + addressList[0].ToString();
IPAddress ip = IPAddress.Parse(addressList[0].ToString());
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(new IPEndPoint(ip, myPort));
serverSocket.Listen(10);
Thread myThread = new Thread(ListenClientConnect);
myThread.Start();
}
// Update is called once per frame
void Update () {
}
void OnGUI()
{
GUILayout.BeginScrollView(new Vector2(0, 0), GUILayout.Width(100));
GUILayout.Box(showMessage);
GUILayout.EndScrollView();
}
private void ListenClientConnect()
{
while (true)
{
Socket clientSocket = serverSocket.Accept();
clientSocket.Send(Encoding.ASCII.GetBytes("Server say Hello"));
Thread receiveThread = new Thread(ReceiveMessage);
receiveThread.Start(clientSocket);
}
}
private void ReceiveMessage(object clientSocket)
{
Debug.Log("进入ReceiveMessage");
Socket myClientSocket = (Socket) clientSocket;
while (true)
{
try
{
int receiveNumber = myClientSocket.Receive(resultBytes);
string contentString = Encoding.ASCII.GetString(resultBytes, 0, receiveNumber);
showMessage += "\n" + contentString;
Debug.Log(contentString);
}
catch (Exception ex)
{
myClientSocket.Shutdown(SocketShutdown.Both);
myClientSocket.Close();
break;
}
}
}
}
传输结构体数据
using System;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PaketHead
{
public UInt32 OPCode;
public byte DiskFlag;
public long DiskSize;
public long OPOffSet;
public long OPByteCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public byte[] Authentic;
public byte Encrypt;
public byte Ver;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public byte[] AddIn;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Reserve;
public byte Zero;
public int SizeOfHead;
}
protected byte[] Struct2Bytes<T>(T obj)
{
int size = Marshal.SizeOf(obj);
byte[] bytes = new byte[size];
IntPtr arrPtr = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0);
Marshal.StructureToPtr(obj, arrPtr, true);
return bytes;
}
protected T Bytes2Struct<T>(byte[] bytes)
{
IntPtr arrPtr = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0);
return (T)Marshal.PtrToStructure(arrPtr, typeof(T));
}
protected void ReadPacketHead(BinaryReader ClientReader, BinaryWriter ClientWriter)
{
byte[] test = null;
test = ClientReader.ReadBytes(180);
PaketHead Paket = Bytes2Struct<PaketHead>(test);
Console.WriteLine(Paket.OPCode);
Console.WriteLine(Paket.DiskFlag);
Console.WriteLine(Paket.DiskSize);
Console.WriteLine(Paket.OPOffSet);
Console.WriteLine(Paket.OPByteCount);
Console.WriteLine(System.Text.Encoding.ASCII.GetString(Paket.Authentic));
Console.WriteLine(Paket.Encrypt);
Console.WriteLine(Paket.Ver);
Console.WriteLine(System.Text.Encoding.ASCII.GetString(Paket.AddIn));
Console.WriteLine(System.Text.Encoding.ASCII.GetString(Paket.Reserve));
Console.WriteLine(Paket.Zero);
Console.WriteLine(Paket.SizeOfHead);
/////////////////////////////////
test = Struct2Bytes<PaketHead>(Paket);
ClientWriter.Write(test);
}
C++客户端代码,传送结构体
#include <winsock2.h>
#pragma comment( lib, "ws2_32.lib" )
#pragma pack(push, 1)//取消内存大小自动对齐
typedef struct _PaketHead2
{
UINT OPCode;/////////////
UCHAR DiskFlag;//////////
__int64 DiskSize;////////
__int64 OPOffSet;////////
__int64 OPByteCount;/////
UCHAR Authentic[64];//
UCHAR Encrypt;////////
UCHAR Ver;////////////
UCHAR AddIn[64];//////
UCHAR Reserve[16];////
UCHAR Zero;///////////
UINT SizeOfHead;/////////
}PaketHead2,*pPaketHead2;
#pragma pack(pop)
//template <class T>
//void ConvertToByteArray(T arg,unsigned char * Buffer)
//{
// for (int i=0;i<sizeof(T); i++)
// {
// int offset = i*8;
// Buffer[i] = (arg& (0xff << offset)) >> offset;
// }
//}
//
//template <class T>
//T ConvertBytesTo(byte *buf)
//{
// T ret = 0x0;
// for (int i=0;i<sizeof(T); i++)
// {
// int offset = i*8;
// ret |= buf[i] << offset;
// }
// return (ret);
//}
int ConnTest()
{
SOCKET mySocket;
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
WSAStartup( wVersionRequested, &wsaData );
try
{
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
printf("Couldn't find a WinSock DLL\n");
return 1;
}
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 2 )
{
printf("Couldn't find the right version for WinSock 2.2\n");
WSACleanup( );
return 1;
}
SOCKADDR_IN ServerAddr;
mySocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(8021);
ServerAddr.sin_addr.s_addr = inet_addr("192.168.0.5");
if (connect(mySocket, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr)))
{
int error_code = WSAGetLastError();
printf("Error connecting socket: %d\n",error_code);
return 1;
}
/////////
PaketHead2 testhead2;
memset(&testhead2,0x00,sizeof(PaketHead2));
testhead2.DiskFlag = 0x1;
testhead2.OPCode = 9856;
testhead2.DiskSize = 78954612;
testhead2.OPOffSet = 98643217;
testhead2.OPByteCount = 85642311;
memcpy(testhead2.Authentic,"9876543210ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghij1234567\0",64);
memcpy(testhead2.AddIn,"9876543210ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghij1234567\0",64);
memcpy(testhead2.Reserve,"abcdefghij12345\0",16);
testhead2.Encrypt = 0x2;
testhead2.Ver = 0x4;
testhead2.Zero = 0x0;
testhead2.SizeOfHead = sizeof(PaketHead2);
send(mySocket,(char*)(&testhead2),sizeof(PaketHead2),NULL);
memset(&testhead2,0x00,sizeof(PaketHead2));
recv(mySocket,(char*)(&testhead2),sizeof(PaketHead2),NULL);
/*testhead2.Authentic[63] = 0;
testhead2.AddIn[63] = 0;
testhead2.Reserve[15] = 0;*/
printf("%d\n",testhead2.OPCode);
printf("%d\n",testhead2.DiskFlag);
printf("%ld\n",testhead2.DiskSize);
printf("%ld\n",testhead2.OPOffSet);
printf("%ld\n",testhead2.OPByteCount);
printf("%s\n",testhead2.Authentic);
printf("%d\n",testhead2.Encrypt);
printf("%d\n",testhead2.Ver);
printf("%s\n",testhead2.AddIn);
printf("%s\n",testhead2.Reserve);
printf("%d\n",testhead2.Zero);
printf("%d\n",testhead2.SizeOfHead);
//////////////////////////////////////////////////
closesocket(mySocket);
WSACleanup( );
}
catch()
{
printf("Error!\n");
}
}